home *** CD-ROM | disk | FTP | other *** search
/ CD ROM Paradise Collection 4 / CD ROM Paradise Collection 4 1995 Nov.iso / graphics / 3dvect37.zip / XMODE.ASM < prev    next >
Assembly Source File  |  1994-06-22  |  115KB  |  3,264 lines

  1. ; ========================================================
  2. ; MODEX.ASM - A Complete Mode X Library
  3. ;
  4. ; Version 1.04 Release, 3 May 1993, By Matt Pritchard
  5. ; With considerable input from Michael Abrash
  6. ;
  7. ; The following information is donated to the public domain in
  8. ; the hopes that save other programmers much frustration.
  9. ;
  10. ; If you do use this code in a product, it would be nice if
  11. ; you include a line like "Mode X routines by Matt Pritchard"
  12. ; in the credits.
  13. ;
  14. ; Protected Mode modification by John McCarthy
  15. ; John McCarthy thanks Matt Pritchard for providing this code,
  16. ; and hopes Matt Pritchard is not offended by the changes.
  17. ;
  18. ; ========================================================
  19. ;
  20. ; All of this code is designed to be assembled with MASM 5.10a
  21. ; but TASM 3.0 could be used as well.
  22. ;
  23. ; The routines contained are designed for use in a MEDIUM model
  24. ; program.  All Routines are FAR, and is assumed that a DGROUP
  25. ; data segment exists and that DS will point to it on entry.
  26. ;
  27. ; For all routines, the AX, BX, CX, DX, ES and FLAGS registers
  28. ; will not be preserved, while the DS, BP, SI and DI registers
  29. ; will be preserved.
  30. ;
  31. ; Unless specifically noted, All Parameters are assumed to be
  32. ; "PASSED BY VALUE".  That is, the actual value is placed on
  33. ; the stack.  When a reference is passed it is assumed to be
  34. ; a near pointer to a variable in the DGROUP segment.
  35. ;
  36. ; Routines that return a single 16-Bit integer value will
  37. ; return that value in the AX register.
  38. ;
  39. ; This code will *NOT* run on an 8086/8088 because 80286+
  40. ; specific instructions are used.   If you have an 8088/86
  41. ; and VGA, you can buy an 80386-40 motherboard for about
  42. ; $160 and move into the 90's.
  43. ;
  44. ; JM - revision, this code will *NOT* run on 80286 because of
  45. ; 80368+ code is used (and protected mode).
  46. ;
  47. ; This code is reasonably optimized: Most drawing loops have
  48. ; been unrolled once and memory references are minimized by
  49. ; keeping stuff in registers when possible.
  50. ;
  51. ; Error Trapping varies by Routine.  No Clipping is performed
  52. ; so the caller should verify that all coordinates are valid.
  53. ;
  54. ; Several Macros are used to simplify common 2 or 3 instruction
  55. ; sequences.  Several Single letter Text Constants also
  56. ; simplify common assembler expressions like "WORD PTR".
  57. ;
  58. ; ------------------ Mode X Variations ------------------
  59. ;
  60. ;  Mode #  Screen Size    Max Pages   Aspect Ratio (X:Y)
  61. ;
  62. ;    0      320 x 200      4 Pages         1.2:1
  63. ;    1      320 x 400      2 Pages         2.4:1
  64. ;    2      360 x 200      3 Pages        1.35:1
  65. ;    3      360 x 400      1 Page          2.7:1
  66. ;    4      320 x 240      3 Pages           1:1
  67. ;    5      320 x 480      1 Page            2:1
  68. ;    6      360 x 240      3 Pages       1.125:1
  69. ;    7      360 x 480      1 Page         2.25:1
  70. ;
  71. ; -------------------- The Legal Stuff ------------------
  72. ;
  73. ; No warranty, either written or implied, is made as to
  74. ; the accuracy and usability of this code product.  Use
  75. ; at your own risk.  Batteries not included.  Pepperoni
  76. ; and extra cheese available for an additional charge.
  77. ;
  78. ; ----------------------- The Author --------------------
  79. ;
  80. ; Matt Pritchard is a paid programmer who'd rather be
  81. ; writing games.  He can be reached at: P.O. Box 140264,
  82. ; Irving, TX  75014  USA.  Michael Abrash is a living
  83. ; god, who now works for Bill Gates (Microsoft).
  84. ;
  85. ; -------------------- Revision History -----------------
  86. ; 4-12-93: v1.02 - SET_POINT & READ_POINT now saves DI
  87. ;          SET_MODEX now saves SI
  88. ; 5-3-93:  v1.04 - added LOAD_DAC_REGISTERS and
  89. ;          READ_DAC_REGISTERS.  Expanded CLR Macro
  90. ;          to handle multiple registers
  91. ;
  92. ; Revisions by John McCarthy - sometime throughout June/93:
  93. ;
  94. ; - Protected mode addressing for all routines.
  95. ; - Conditional assembley for routines not wanted by user
  96. ; - Bios text addressing removed - see set_vga_modex
  97. ; - File assembles for protected mode, variables and routines made public
  98. ; - Mode03 routine added to return to DOS mode.  Must be used in conjunction
  99. ;      with TRAN's protected mode header.
  100. ; - Also added routines to turn off the screen to avoid that screen  flicker
  101. ;      when changing into xmode.
  102. ;
  103. ; A note from John McCarthy:
  104. ;
  105. ;  When converting to protected mode, loop's, rep's, stosb's and such
  106. ;  must have the high word of the ecx,edi,esi registers set.  I have tested
  107. ;  the routines now that they have been converted and they seem to all work
  108. ;  fine, but if you find they don't jive, please send me a message/letter and
  109. ;  I will get right on it.
  110. ;
  111. ;  These routines originally came with all kinds of extra files for font
  112. ;  editing, pallet editing, C stuff and demos.  I have only supplied the
  113. ;  critical files from Matt to cut down on .zip file size.
  114. ;
  115.  
  116. x_fill_block    equ 1       ; small routines are automatically included
  117. x_draw_line     equ 1       ; which x mode routines should be included
  118. x_set_point     equ 1       ; when assembled; 1 = enabled
  119. x_read_point    equ 0
  120. x_gprintc       equ 0
  121. x_tgprintc      equ 1
  122. x_set_window    equ 0
  123. x_print_str     equ 0
  124. x_tprint_str    equ 0
  125. x_set_font      equ 0
  126. x_draw_bitmap   equ 0
  127. x_tdraw_bitmap  equ 1
  128. x_copy_page     equ 0
  129. x_copy_bitmap   equ 0
  130.  
  131.            .386p
  132.            jumps
  133.  
  134. code32     segment para public use32
  135.            assume cs:code32, ds:code32
  136.  
  137.            include pmode.ext     ; protected mode externals
  138.            include macros.inc    ; guess...
  139.            include equ.inc       ; list of constants
  140.  
  141. ; xmode routines by matt prichard
  142. ; modified for 386 protected mode by john mccarthy
  143. ; protected mode header courtesy of TRAN
  144.  
  145. ; ===== data tables =====
  146.  
  147. ; bit mask tables for left/right/character masks
  148.  
  149.            public left_clip_mask
  150.            public right_clip_mask
  151.  
  152. left_clip_mask  db 0fh, 0eh, 0ch, 08h
  153. right_clip_mask db 01h, 03h, 07h, 0fh
  154.  
  155. ; bit patterns for converting character fonts
  156.  
  157. char_plane_data db 00h,08h,04h,0ch,02h,0ah,06h,0eh
  158.                 db 01h,09h,05h,0dh,03h,0bh,07h,0fh
  159.  
  160. ; crtc register values for various configurations
  161.  
  162. mode_single_line:       ; crtc setup data for 400/480 line modes
  163.         dw  04009h      ; cell height (1 scan line)
  164.         dw  00014h      ; dword mode off
  165.         dw  0e317h      ; turn on byte mode
  166.         dw  nil         ; end of crtc data for 400/480 line mode
  167.  
  168. mode_double_line:       ; crtc setup data for 200/240 line modes
  169.         dw  04109h      ; cell height (2 scan lines)
  170.         dw  00014h      ; dword mode off
  171.         dw  0e317h      ; turn on byte mode
  172.         dw  nil         ; end of crtc data for 200/240 line mode
  173.  
  174. mode_320_wide:          ; crtc setup data for 320 horz pixels
  175.         dw  05f00h      ; horz total
  176.         dw  04f01h      ; horz displayed
  177.         dw  05002h      ; start horz blanking
  178.         dw  08203h      ; end horz blanking
  179.         dw  05404h      ; start h sync
  180.         dw  08005h      ; end h sync
  181.         dw  nil         ; end of crtc data for 320 horz pixels
  182.  
  183. mode_360_wide:          ; crtc setup data for 360 horz pixels
  184.         dw  06b00h      ; horz total
  185.         dw  05901h      ; horz displayed
  186.         dw  05a02h      ; start horz blanking
  187.         dw  08e03h      ; end horz blanking
  188.         dw  05e04h      ; start h sync
  189.         dw  08a05h      ; end h sync
  190.         dw  nil         ; end of crtc data for 360 horz pixels
  191.  
  192. mode_200_tall:
  193. mode_400_tall:          ; crtc setup data for 200/400 line modes
  194.         dw  0bf06h      ; vertical total
  195.         dw  01f07h      ; overflow
  196.         dw  09c10h      ; v sync start
  197.         dw  08e11h      ; v sync end/prot cr0 cr7
  198.         dw  08f12h      ; vertical displayed
  199.         dw  09615h      ; v blank start
  200.         dw  0b916h      ; v blank end
  201.         dw  nil         ; end of crtc data for 200/400 lines
  202.  
  203. mode_240_tall:
  204. mode_480_tall:          ; crtc setup data for 240/480 line modes
  205.         dw  00d06h      ; vertical total
  206.         dw  03e07h      ; overflow
  207.         dw  0ea10h      ; v sync start
  208.         dw  08c11h      ; v sync end/prot cr0 cr7
  209.         dw  0df12h      ; vertical displayed
  210.         dw  0e715h      ; v blank start
  211.         dw  00616h      ; v blank end
  212.         dw  nil         ; end of crtc data for 240/480 lines
  213.  
  214. ; table of display mode tables
  215.  
  216. mode_table:
  217.         dd  o mode_320x200, o mode_320x400
  218.         dd  o mode_360x200, o mode_360x400
  219.         dd  o mode_320x240, o mode_320x480
  220.         dd  o mode_360x240, o mode_360x480
  221.  
  222. ; table of display mode components
  223.  
  224. mode_320x200:           ; data for 320 by 200 pixels
  225.  
  226.         db  063h        ; 400 scan lines & 25 mhz clock
  227.         db  4           ; maximum of 4 pages
  228.         dw  320, 200    ; displayed pixels (x,y)
  229.         dw  1302, 816   ; max possible x and y sizes
  230.  
  231.         dd  o mode_320_wide, o mode_200_tall
  232.         dd  o mode_double_line, nil
  233.  
  234. mode_320x400:           ; data for 320 by 400 pixels
  235.  
  236.         db  063h        ; 400 scan lines & 25 mhz clock
  237.         db  2           ; maximum of 2 pages
  238.         dw  320, 400    ; displayed pixels x,y
  239.         dw  648, 816    ; max possible x and y sizes
  240.  
  241.         dd  o mode_320_wide, o mode_400_tall
  242.         dd  o mode_single_line, nil
  243.  
  244. mode_360x240:           ; data for 360 by 240 pixels
  245.  
  246.         db  0e7h        ; 480 scan lines & 28 mhz clock
  247.         db  3           ; maximum of 3 pages
  248.         dw  360, 240    ; displayed pixels x,y
  249.         dw  1092, 728   ; max possible x and y sizes
  250.  
  251.         dd  o mode_360_wide, o mode_240_tall
  252.         dd  o mode_double_line , nil
  253.  
  254. mode_360x480:           ; data for 360 by 480 pixels
  255.  
  256.         db  0e7h        ; 480 scan lines & 28 mhz clock
  257.         db  1           ; only 1 page possible
  258.         dw  360, 480    ; displayed pixels x,y
  259.         dw  544, 728    ; max possible x and y sizes
  260.  
  261.         dd  o mode_360_wide, o mode_480_tall
  262.         dd  o mode_single_line , nil
  263.  
  264. mode_320x240:           ; data for 320 by 240 pixels
  265.  
  266.         db  0e3h        ; 480 scan lines & 25 mhz clock
  267.         db  3           ; maximum of 3 pages
  268.         dw  320, 240    ; displayed pixels x,y
  269.         dw  1088, 818   ; max possible x and y sizes
  270.  
  271.         dd  o mode_320_wide, o mode_240_tall
  272.         dd  o mode_double_line, nil
  273.  
  274. mode_320x480:           ; data for 320 by 480 pixels
  275.  
  276.         db  0e3h        ; 480 scan lines & 25 mhz clock
  277.         db  1           ; only 1 page possible
  278.         dw  320, 480    ; displayed pixels x,y
  279.         dw  540, 818    ; max possible x and y sizes
  280.  
  281.         dd  o mode_320_wide, o mode_480_tall
  282.         dd  o mode_single_line, nil
  283.  
  284. mode_360x200:           ; data for 360 by 200 pixels
  285.  
  286.         db  067h        ; 400 scan lines & 28 mhz clock
  287.         db  3           ; maximum of 3 pages
  288.         dw  360, 200    ; displayed pixels (x,y)
  289.         dw  1302, 728   ; max possible x and y sizes
  290.  
  291.         dd  o mode_360_wide, o mode_200_tall
  292.         dd  o mode_double_line, nil
  293.  
  294. mode_360x400:           ; data for 360 by 400 pixels
  295.  
  296.         db  067h        ; 400 scan lines & 28 mhz clock
  297.         db  1           ; maximum of 1 pages
  298.         dw  360, 400    ; displayed pixels x,y
  299.         dw  648, 816    ; max possible x and y sizes
  300.  
  301.         dd  o mode_360_wide, o mode_400_tall
  302.         dd  o mode_single_line, nil
  303.  
  304. mode_data_table struc
  305.     m_miscr         db  ?       ; value of misc_output register
  306.     m_pages         db  ?       ; maximum possible # of pages
  307.     m_xsize         dw  ?       ; x size displayed on screen
  308.     m_ysize         dw  ?       ; y size displayed on screen
  309.     m_xmax          dw  ?       ; maximum possible x size
  310.     m_ymax          dw  ?       ; maximum possible y size
  311.     m_crtc          dd  ?       ; table of crtc register values
  312. mode_data_table ends
  313.  
  314. ; specific x mode data table format...
  315.  
  316. screen_width    dw  0        ; actual width of a line in bytes
  317. screen_height   dw  0        ; actual vertical height in pixels
  318.  
  319. last_page       dw  0        ; # of display pages
  320. page_addr       dd  4 dup (0) ; offsets to start of each page
  321.  
  322. page_size       dw  0        ; size of page in addr bytes
  323.  
  324. display_page    dw  0        ; page # currently displayed
  325. active_page     dw  0        ; page # currently active
  326.  
  327. current_page    dd  0        ; address of current page
  328.  
  329. current_xoffset dw  0        ; current display x offset
  330. current_yoffset dw  0        ; current display y offset
  331.  
  332. current_moffset dd  0        ; current start offset
  333.  
  334. max_xoffset     dw  0        ; current display x offset
  335. max_yoffset     dw  0        ; current display y offset
  336.  
  337. charset_low     dd  0        ; far ptr to char set: 0-127
  338. charset_hi      dd  0        ; far ptr to char set: 128-255
  339.  
  340.         public  screen_width
  341.         public  screen_height
  342.  
  343.         public  last_page
  344.         public  page_addr
  345.  
  346.         public  page_size
  347.  
  348.         public  display_page
  349.         public  active_page
  350.  
  351.         public  current_page
  352.  
  353.         public  current_xoffset
  354.         public  current_yoffset
  355.  
  356.         public  current_moffset
  357.  
  358.         public  max_xoffset
  359.         public  max_yoffset
  360.  
  361.         public  charset_low
  362.         public  charset_hi
  363.  
  364.     ; ===== mode x setup routines =====
  365.  
  366. ;======================================================
  367. ;set_vga_modex% (modetype%, maxxpos%, maxypos%, pages%)
  368. ;======================================================
  369. ;
  370. ; sets up the specified version of mode x.  allows for
  371. ; the setup of multiple video pages, and a virtual
  372. ; screen which can be larger than the displayed screen
  373. ; (which can then be scrolled a pixel at a time)
  374. ;
  375. ; entry: modetype = desired screen resolution (0-7)
  376. ;
  377. ;     0 =  320 x 200, 4 pages max,   1.2:1 aspect ratio
  378. ;     1 =  320 x 400, 2 pages max,   2.4:1 aspect ratio
  379. ;     2 =  360 x 200, 3 pages max,  1.35:1 aspect ratio
  380. ;     3 =  360 x 400, 1 page  max,   2.7:1 aspect ratio
  381. ;     4 =  320 x 240, 3 pages max,     1:1 aspect ratio
  382. ;     5 =  320 x 480, 1 page  max,     2:1 aspect ratio
  383. ;     6 =  360 x 240, 3 pages max, 1.125:1 aspect ratio
  384. ;     7 =  360 x 480, 1 page  max,  2.25:1 aspect ratio
  385. ;
  386. ;        maxxpos = the desired virtual screen width
  387. ;        maxypos = the desired virtual screen height
  388. ;        pages   = the desired # of video pages
  389. ;
  390. ; exit:  ax = success flag:   0 = failure / -1= success
  391. ;
  392.  
  393. svm_stack   struc
  394.     svm_table   dd  ?   ; offset of mode info table
  395.                 dd  ?x3 ; edi, esi, ebp
  396.                 dd  ?   ; caller
  397.     svm_pages   dw  ?   ; # of screen pages desired
  398.     svm_ysize   dw  ?   ; vertical screen size desired
  399.     svm_xsize   dw  ?   ; horizontal screen size desired
  400.     svm_mode    dw  ?   ; display resolution desired
  401. svm_stack   ends
  402.  
  403.     public  set_vga_modex
  404.  
  405. set_vga_modex:
  406.     push    ebp esi edi         ; preserve important registers
  407.     sub     esp, 4              ; allocate workspace
  408.     mov     ebp, esp            ; set up stack frame
  409.  
  410. ; check legality of mode request....
  411.  
  412.     movzx   ebx, [ebp].svm_mode ; get requested mode #
  413.  
  414.     cmp     bx, num_modes       ; is it 0..7?
  415.     jae     @svm_badmodesetup   ; if not, error out
  416.  
  417.     shl     bx, 2                  ; scale bx
  418.     mov     esi, d mode_table[ebx] ; si -> mode info
  419.     mov     [ebp].svm_table, esi   ; save ptr for later use
  420.  
  421. ; check # of requested display pages
  422.  
  423.     mov     cx, [ebp].svm_pages ; get # of requested pages
  424.  
  425.     clr     ch                  ; set hi word = 0!
  426.     cmp     cl, [esi].m_pages   ; check # pages for mode
  427.  
  428.     ja      @svm_badmodesetup   ; report error if too many pages
  429.     jcxz    @svm_badmodesetup   ; report error if 0 pages
  430.  
  431. ; check validity of x size
  432.  
  433.     and     [ebp].svm_xsize, 0fff8h  ; x size mod 8 must = 0
  434.  
  435.     mov     ax, [ebp].svm_xsize ; get logical screen width
  436.  
  437.     cmp     ax, [esi].m_xsize   ; check against displayed x
  438.     jb      @svm_badmodesetup   ; report error if too small
  439.     cmp     ax, [esi].m_xmax    ; check against max x
  440.     ja      @svm_badmodesetup   ; report error if too big
  441.  
  442. ; check validity of y size
  443.  
  444.     mov     bx, [ebp].svm_ysize ; get logical screen height
  445.     cmp     bx, [esi].m_ysize   ; check against displayed y
  446.     jb      @svm_badmodesetup   ; report error if too small
  447.     cmp     bx, [esi].m_ymax    ; check against max y
  448.     ja      @svm_badmodesetup   ; report error if too big
  449.  
  450. ; enough memory to fit it all?
  451.  
  452.     shr     ax, 2               ; # of bytes:line = xsize/4
  453.     mul     cx                  ; ax = bytes/line * pages
  454.     mul     bx                  ; dx:ax = total vga mem needed
  455.     jno     @svm_continue       ; exit if total size > 256k
  456.  
  457.     dec     dx                  ; was it exactly 256k???
  458.     or      dx, ax              ; (dx = 1, ax = 0000)
  459.     jz      @svm_continue       ; if so, it's valid...
  460.  
  461. @svm_badmodesetup:
  462.  
  463.     clr     ax                  ; return value = false
  464.     jmp     @svm_exit           ; normal exit
  465.  
  466. @svm_continue:
  467.  
  468.     mov     v86r_ax,13h         ; start with mode 13h
  469.     mov     al,10h              ; bios int 10h
  470.     int     33h                 ; let v86 handler call bios int 10h
  471.  
  472.     call turn_screen_off        ; prevent flicker
  473.  
  474.     out_16  sc_index, chain4_off        ; disable chain 4 mode
  475.     out_16  sc_index, async_reset       ; (a)synchronous reset
  476.     out_8   misc_output, [esi].m_miscr  ; set new timing/size
  477.     out_16  sc_index, sequ_restart      ; restart sequencer ...
  478.  
  479.     out_8   crtc_index, 11h     ; select vert retrace end register
  480.     inc     dx                  ; point to data
  481.     in      al, dx              ; get value, bit 7 = protect
  482.     and     al, 7fh             ; mask out write protect
  483.     out     dx, al              ; and send it back
  484.  
  485.     mov     dx, crtc_index      ; vga crtc registers
  486.     add     esi, m_crtc         ; si -> crtc parameter data
  487.  
  488. ; load tables of crtc parameters from list of tables
  489.  
  490. @svm_setup_table:
  491.  
  492.     mov     edi, [esi]          ; get pointer to crtc data tbl
  493.     add     esi, 4              ; point to next ptr entry
  494.     or      edi, edi            ; a nil ptr means that we have
  495.     jz      @svm_set_data       ; finished crtc programming
  496.  
  497. @svm_setup_crtc:
  498.     mov     ax, [edi]           ; get crtc data from table
  499.     add     edi, 2              ; advance pointer
  500.     or      ax, ax              ; at end of data table?
  501.     jz      @svm_setup_table    ; if so, exit & get next table
  502.  
  503.     out     dx, ax              ; reprogram vga crtc reg
  504.     jmp     s @svm_setup_crtc   ; process next table entry
  505.  
  506. ; initialize page & scroll info, edi = 0
  507.  
  508. @svm_set_data:
  509.     mov     display_page, di    ; display page = 0
  510.     mov     active_page, di     ; active page = 0
  511.     mov     current_xoffset, di ; horz scroll index = 0
  512.     mov     current_yoffset, di ; vert scroll index = 0
  513.     mov     current_moffset,edi ; memory scroll index = 0
  514.  
  515.     @rlp    esi, vga_segment    ; segment for vga memory
  516.     mov     current_page, esi
  517.  
  518. ; set logical screen width, x scroll and our data
  519.  
  520.     mov     esi, [ebp].svm_table ; get saved ptr to mode info
  521.     mov     ax, [ebp].svm_xsize ; get display width
  522.  
  523.     mov     cx, ax              ; cx = logical width
  524.     sub     cx, [esi].m_xsize   ; cx = max x scroll value
  525.     mov     max_xoffset, cx     ; set maximum x scroll
  526.  
  527.     shr     ax, 2               ; bytes = pixels / 4
  528.     mov     screen_width, ax    ; save width in pixels
  529.  
  530.     shr     ax, 1               ; offset value = bytes / 2
  531.     mov     ah, 13h             ; crtc offset register index
  532.     xchg    al, ah              ; switch format for out
  533.     out     dx, ax              ; set vga crtc offset reg
  534.  
  535. ; setup data table, y scroll, misc for other routines
  536.  
  537.     mov     ax, [ebp].svm_ysize ; get logical screen height
  538.  
  539.     mov     cx, ax              ; cx = logical height
  540.     sub     bx, [esi].m_ysize   ; cx = max y scroll value
  541.     mov     max_yoffset, cx     ; set maximum y scroll
  542.  
  543.     mov     screen_height, ax   ; save height in pixels
  544.     mul     screen_width        ; ax = page size in bytes,
  545.     mov     page_size, ax       ; save page size
  546.  
  547.     mov     cx, [ebp].svm_pages ; get # of pages
  548.     mov     last_page, cx       ; save # of pages
  549.  
  550.     clr     bx                  ; page # = 0
  551.     @rlp    edx, vga_segment    ; page 0 offset = 0a0000h
  552.     movzx   eax,ax              ; clear top word of eax
  553.     movzx   ebx,bx
  554.  
  555. @svm_set_pages:
  556.  
  557.     mov     page_addr[ebx], edx ; set page #(bx) offset
  558.     add     bx, 4               ; page#++
  559.     add     edx, eax            ; compute addr of next page
  560.     loopx   cx, @svm_set_pages  ; loop until all pages set
  561.  
  562. ; clear vga memory
  563.  
  564.     out_16  sc_index, all_planes_on ; select all planes
  565.     mov     edi, current_page   ; -> start of vga memory
  566.  
  567.     clr     ax                  ; ax = 0
  568.     cld                         ; block xfer forwards
  569.     mov     ecx, 8000h          ; 32k * 4 * 2 = 256k
  570.     rep     stosw               ; clear dat memory!
  571.  
  572. ; setup font pointers, in protected mode.  you must shl 8 then add but
  573. ; i don't know how to correctly convert segmented code to flat memory.
  574. ;
  575. ;   mov     v86r_bh, rom_8x8_lo    ; ask for 8x8 font, 0-127
  576. ;   mov     v86r_ax, get_char_ptr  ; service to get pointer
  577. ;   mov     al,10h
  578. ;   int     33h                 ; call vga bios through v86 handler
  579. ;
  580. ;   mov     charset_low, bp     ; save char set offset
  581. ;   mov     charset_low+2, es   ; save char set segment
  582. ;
  583. ;   mov     v86r_bh, rom_8x8_hi    ; ask for 8x8 font, 128-255
  584. ;   mov     v86r_ax, get_char_ptr  ; service to get pointer
  585. ;   mov     al,10h
  586. ;   int     33h                 ; call vga bios
  587. ;
  588. ;   mov     charset_hi, bp      ; save char set offset
  589. ;   mov     charset_hi+2, es    ; save char set segment
  590.  
  591.     call turn_screen_on         ; prevent flicker
  592.  
  593.     mov     ax, true            ; return success code
  594.  
  595. @svm_exit:
  596.     add     esp, 4              ; deallocate workspace
  597.     pop     edi esi ebp         ; restore saved registers
  598.     ret     8                   ; exit & clean up stack
  599.  
  600. ;==================
  601. ;set_modex% (mode%)
  602. ;==================
  603. ;
  604. ; quickie mode set - sets up mode x to default configuration
  605. ;
  606. ; entry: modetype = desired screen resolution (0-7)
  607. ;        (see set_vga_modex for list)
  608. ;
  609. ; exit:  ax = success flag:   0 = failure / -1= success
  610. ;
  611.  
  612. sm_stack    struc
  613.                 dd  ?,? ; ebp, esi
  614.                 dd  ?   ; caller
  615.     sm_mode     dw  ?   ; desired screen resolution
  616. sm_stack    ends
  617.  
  618.     public  set_modex
  619.  
  620. set_modex:
  621.     push    ebp esi             ; preserve important registers
  622.     mov     ebp, esp            ; set up stack frame
  623.  
  624.     clr     ax                  ; assume failure
  625.     movzx   ebx, [ebp].sm_mode  ; get desired mode #
  626.     cmp     bx, num_modes       ; is it a valid mode #?
  627.     jae     @smx_exit           ; if not, don't bother
  628.  
  629.     push    bx                  ; push mode parameter
  630.  
  631.     shl     bx, 2                 ; scale bx to dword indexer
  632.     mov     esi, d mode_table[ebx] ; esi -> mode info
  633.  
  634.     push    [esi].m_xsize       ; push default x size
  635.     push    [esi].m_ysize       ; push default y size
  636.     mov     al, [esi].m_pages   ; get default # of pages
  637.     clr     ah                  ; hi byte = 0
  638.     push    ax                  ; push # pages
  639.  
  640.     call    set_vga_modex       ; set up mode x!
  641.  
  642. @smx_exit:
  643.     pop     esi ebp             ; restore registers
  644.     ret     2                   ; exit & clean up stack
  645.  
  646. ; ===== basic graphics primitives =====
  647.  
  648. ;============================
  649. ;clear_vga_screen (colornum%)
  650. ;============================
  651. ;
  652. ; clears the active display page
  653. ;
  654. ; entry: colornum = color value to fill the page with
  655. ;
  656. ; exit:  no meaningful values returned
  657. ;
  658.  
  659. cvs_stack   struc
  660.                 dd  ?,? ; edi, ebp
  661.                 dd  ?   ; caller
  662.     cvs_color   db  ?,? ; color to set screen to
  663. cvs_stack   ends
  664.  
  665.     public  clear_vga_screen
  666.  
  667. clear_vga_screen:
  668.  
  669.     push    ebp edi             ; preserve important registers
  670.     mov     ebp, esp            ; set up stack frame
  671.  
  672.     out_16  sc_index, all_planes_on ; select all planes
  673.     mov     edi, current_page   ; point to active vga page
  674.  
  675.     mov     al, [ebp].cvs_color ; get color
  676.     mov     ah, al              ; copy for dword write
  677.     mov     bp,ax
  678.     shl     eax,16
  679.     mov     ax,bp
  680.     cld                         ; block fill forwards
  681.  
  682.     movzx   ecx, page_size      ; get size of page
  683.     shr     cx, 2               ; divide by 4 for dwords
  684.     rep     stosd               ; block fill vga memory
  685.  
  686.     pop     edi ebp             ; restore saved registers
  687.     ret     2                   ; exit & clean up stack
  688.  
  689. ;===================================
  690. ;set_point (xpos%, ypos%, colornum%)
  691. ;===================================
  692. ;
  693. ; plots a single pixel on the active display page
  694. ;
  695. ; entry: xpos     = x position to plot pixel at
  696. ;        ypos     = y position to plot pixel at
  697. ;        colornum = color to plot pixel with
  698. ;
  699. ; exit:  no meaningful values returned
  700. ;
  701.  
  702. sp_stack    struc
  703.                 dd  ?,? ; ebp, edi
  704.                 dd  ?   ; caller
  705.     setp_color  db  ?,? ; color of point to plot
  706.     setp_ypos   dw  ?   ; y pos of point to plot
  707.     setp_xpos   dw  ?   ; x pos of point to plot
  708. sp_stack    ends
  709.  
  710.         public set_point
  711.  
  712. set_point:
  713.  
  714.     push    ebp edi             ; preserve registers
  715.     mov     ebp, esp            ; set up stack frame
  716.  
  717.     mov     edi, current_page   ; point to active vga page
  718.  
  719.     mov     ax, [ebp].setp_ypos ; get line # of pixel
  720.     mul     screen_width        ; get offset to start of line
  721.  
  722.     clr     ebx                 ; wipe high word
  723.     mov     bx, [ebp].setp_xpos ; get xpos
  724.     mov     cx, bx              ; copy to extract plane # from
  725.     shr     bx, 2               ; x offset (bytes) = xpos/4
  726.     add     bx, ax              ; offset = width*ypos + xpos/4
  727.  
  728.     mov     ax, map_mask_plane1 ; map mask & plane select register
  729.     and     cl, plane_bits      ; get plane bits
  730.     shl     ah, cl              ; get plane select value
  731.     out_16  sc_index, ax        ; select plane
  732.  
  733.     mov     al,[ebp].setp_color ; get pixel color
  734.     mov     [edi+ebx], al       ; draw pixel
  735.  
  736.     pop     edi ebp             ; restore saved registers
  737.     ret     6                   ; exit and clean up stack
  738.  
  739. ;==========================
  740. ;read_point% (xpos%, ypos%)
  741. ;==========================
  742. ;
  743. ; read the color of a pixel from the active display page
  744. ;
  745. ; entry: xpos = x position of pixel to read
  746. ;        ypos = y position of pixel to read
  747. ;
  748. ; exit:  ax   = color of pixel at (xpos, ypos)
  749. ;
  750.  
  751. rp_stack    struc
  752.             dd  ?,? ; ebp, edi
  753.             dd  ?   ; caller
  754.     rp_ypos dw  ?   ; y pos of point to read
  755.     rp_xpos dw  ?   ; x pos of point to read
  756. rp_stack    ends
  757.  
  758.         public  read_point
  759.  
  760. read_point:
  761.  
  762.     push    ebp edi             ; preserve registers
  763.     mov     ebp, esp            ; set up stack frame
  764.  
  765.     mov     edi, current_page   ; point to active vga page
  766.  
  767.     mov     ax, [ebp].rp_ypos   ; get line # of pixel
  768.     mul     screen_width        ; get offset to start of line
  769.  
  770.     clr     ebx                 ; wipe high word
  771.     mov     bx, [ebp].rp_xpos   ; get xpos
  772.     mov     cx, bx
  773.     shr     bx, 2               ; x offset (bytes) = xpos/4
  774.     add     bx, ax              ; offset = width*ypos + xpos/4
  775.  
  776.     mov     al, read_map        ; gc read mask register
  777.     mov     ah, cl              ; get xpos
  778.     and     ah, plane_bits      ; & mask out plane #
  779.     out_16  gc_index, ax        ; select plane to read in
  780.  
  781.     clr     eax                 ; clear return value hi byte
  782.     mov     al, [edi+ebx]       ; get color of pixel
  783.  
  784.     pop     edi ebp             ; restore saved registers
  785.     ret     4                   ; exit and clean up stack
  786.  
  787.     if x_fill_block eq 1
  788.  
  789. ;======================================================
  790. ;fill_block (xpos1%, ypos1%, xpos2%, ypos2%, colornum%)
  791. ;======================================================
  792. ;
  793. ; fills a rectangular block on the active display page
  794. ;
  795. ; entry: xpos1    = left x position of area to fill
  796. ;        ypos1    = top y position of area to fill
  797. ;        xpos2    = right x position of area to fill
  798. ;        ypos2    = bottom y position of area to fill
  799. ;        colornum = color to fill area with
  800. ;
  801. ; exit:  no meaningful values returned
  802. ;
  803.  
  804. fb_stack    struc
  805.                 dd  ?x3 ; edi, esi, ebp
  806.                 dd  ?   ; caller
  807.     fb_color    db  ?,? ; fill color
  808.     fb_ypos2    dw  ?   ; y pos of lower right pixel
  809.     fb_xpos2    dw  ?   ; x pos of lower right pixel
  810.     fb_ypos1    dw  ?   ; y pos of upper left pixel
  811.     fb_xpos1    dw  ?   ; x pos of upper left pixel
  812. fb_stack    ends
  813.  
  814.         public    fill_block
  815.  
  816. fill_block:
  817.  
  818.     push    ebp esi edi         ; preserve important registers
  819.     mov     ebp, esp            ; set up stack frame
  820.  
  821.     mov     edi, current_page   ; point to active vga page
  822.     cld                         ; direction flag = forward
  823.  
  824.     out_8   sc_index, map_mask  ; set up for plane select
  825.  
  826. ; validate pixel coordinates
  827. ; if necessary, swap so x1 <= x2, y1 <= y2
  828.  
  829.     clr     eax
  830.     clr     ecx
  831.     mov     ax, [ebp].fb_ypos1  ; ax = y1   is y1< y2?
  832.     mov     bx, [ebp].fb_ypos2  ; bx = y2
  833.     cmp     ax, bx
  834.     jle     @fb_noswap1
  835.  
  836.     mov     [ebp].fb_ypos1, bx  ; swap y1 and y2 and save y1
  837.     xchg    ax, bx              ; on stack for future use
  838.  
  839. @fb_noswap1:
  840.     sub     bx, ax              ; get y width
  841.     inc     bx                  ; add 1 to avoid 0 value
  842.     mov     [ebp].fb_ypos2, bx  ; save in ypos2
  843.  
  844.     mul     screen_width        ; mul y1 by bytes per line
  845.     add     edi, eax            ; di = start of line y1
  846.  
  847.     mov     ax, [ebp].fb_xpos1  ; check x1 <= x2
  848.     mov     bx, [ebp].fb_xpos2  ;
  849.     cmp     ax, bx
  850.     jle     @fb_noswap2         ; skip ahead if ok
  851.  
  852.     mov     [ebp].fb_xpos2, ax  ; swap x1 and x2 and save x2
  853.     xchg    ax, bx              ; on stack for future use
  854.  
  855. ; all our input values are in  order,  now  determine
  856. ; how many full "bands" 4 pixels wide (aligned) there
  857. ; are, and if there are partial bands (<4 pixels)  on
  858. ; the left and right edges.
  859.  
  860. @fb_noswap2:
  861.     movzx   edx, ax             ; dx = x1 (pixel position)
  862.     shr     edx, 2              ; dx/4 = bytes into line
  863.     add     edi, edx            ; di = addr of upper-left corner
  864.  
  865.     movzx   ecx, bx             ; cx = x2 (pixel position)
  866.     shr     cx, 2               ; cx/4 = bytes into line
  867.  
  868.     cmp     dx, cx              ; start and end in same band?
  869.     jne     @fb_normal          ; if not, check for l & r edges
  870.     jmp     @fb_one_band_only   ; if so, then special processing
  871.  
  872. @fb_normal:
  873.     sub     cx, dx              ; cx = # bands -1
  874.     movzx   esi, ax             ; si = plane#(x1)
  875.     and     si, plane_bits      ; if left edge is aligned then
  876.     jz      @fb_l_plane_flush   ; no special processing..
  877.  
  878. ; draw "left edge" vertical strip of 1-3 pixels...
  879.  
  880.     out_8   sc_data, left_clip_mask[esi] ; set left edge plane mask
  881.  
  882.     mov     esi, edi            ; si = copy of start addr (ul)
  883.  
  884.     mov     dx, [ebp].fb_ypos2  ; get # of lines to draw
  885.     mov     al, [ebp].fb_color  ; get fill color
  886.     movzx   ebx, screen_width   ; get vertical increment value
  887.  
  888. @fb_left_loop:
  889.     mov     [esi], al           ; fill in left edge pixels
  890.     add     esi, ebx            ; point to next line (below)
  891.     loopjz  dx, @fb_left_cont   ; exit loop if all lines drawn
  892.  
  893.     mov     [esi], al           ; fill in left edge pixels
  894.     add     esi, ebx            ; point to next line (below)
  895.     loopx   dx, @fb_left_loop   ; loop until left strip is drawn
  896.  
  897. @fb_left_cont:
  898.  
  899.     inc     edi                 ; point to middle (or right) block
  900.     dec     cx                  ; reset cx instead of jmp @fb_right
  901.  
  902. @fb_l_plane_flush:
  903.     inc     cx                  ; add in left band to middle block
  904.  
  905. ; di = addr of 1st middle pixel (band) to fill
  906. ; cx = # of bands to fill -1
  907.  
  908. @fb_right:
  909.     movzx   esi, [ebp].fb_xpos2 ; get xpos2
  910.     and     si, plane_bits      ; get plane values
  911.     cmp     si, 0003            ; plane = 3?
  912.     je      @fb_r_edge_flush    ; hey, add to middle
  913.  
  914. ; draw "right edge" vertical strip of 1-3 pixels...
  915.  
  916.     out_8   sc_data, right_clip_mask[esi]    ; right edge plane mask
  917.  
  918.     mov     esi, edi            ; get addr of left edge
  919.     add     esi, ecx            ; add width-1 (bands)
  920.     dec     esi                 ; to point to top of right edge
  921.  
  922.     mov     dx, [ebp].fb_ypos2  ; get # of lines to draw
  923.     mov     al, [ebp].fb_color  ; get fill color
  924.     movzx   ebx, screen_width   ; get vertical increment value
  925.  
  926. @fb_right_loop:
  927.     mov     [esi], al           ; fill in right edge pixels
  928.     add     esi, ebx            ; point to next line (below)
  929.     loopjz  dx, @fb_right_cont  ; exit loop if all lines drawn
  930.  
  931.     mov     [esi], al            ; fill in right edge pixels
  932.     add     esi, ebx            ; point to next line (below)
  933.     loopx   dx, @fb_right_loop  ; loop until left strip is drawn
  934.  
  935. @fb_right_cont:
  936.  
  937.     dec     cx                  ; minus 1 for middle bands
  938.     jz      @fb_exit            ; uh.. no middle bands...
  939.  
  940. @fb_r_edge_flush:
  941.  
  942. ; di = addr of upper left block to fill
  943. ; cx = # of bands to fill in (width)
  944.  
  945.     out_8   sc_data, all_planes ; write to all planes
  946.  
  947.     mov     dx, screen_width    ; dx = di increment
  948.     sub     dx, cx              ; = screen_width-# planes filled
  949.  
  950.     mov     ebx, ecx            ; bx = quick refill for cx
  951.     mov     si, [ebp].fb_ypos2  ; si = # of line to fill
  952.     mov     al, [ebp].fb_color  ; get fill color
  953.  
  954. @fb_middle_loop:
  955.     rep     stosb               ; fill in entire line
  956.  
  957.     mov     ecx, ebx            ; recharge cx (line width)
  958.     add     edi, edx            ; point to start of next line
  959.     loopx   si, @fb_middle_loop ; loop until all lines drawn
  960.  
  961.     jmp     s @fb_exit          ; outa here
  962.  
  963. @fb_one_band_only:
  964.     movzx   esi, ax                 ; get left clip mask, save x1
  965.     and     si, plane_bits          ; mask out row #
  966.     mov     al, left_clip_mask[esi] ; get left edge mask
  967.     mov     si, bx                  ; get right clip mask, save x2
  968.     and     si, plane_bits          ; mask out row #
  969.     and     al, right_clip_mask[esi] ; get right edge mask byte
  970.  
  971.     out_8   sc_data, al         ; clip for left & right masks
  972.  
  973.     mov     cx, [ebp].fb_ypos2  ; get # of lines to draw
  974.     mov     al, [ebp].fb_color  ; get fill color
  975.     clr     ebx                 ; wipe high word
  976.     mov     bx, screen_width    ; get vertical increment value
  977.  
  978. @fb_one_loop:
  979.     mov     [edi], al           ; fill in pixels
  980.     add     edi, ebx            ; point to next line (below)
  981.     loopjz  cx, @fb_exit        ; exit loop if all lines drawn
  982.  
  983.     mov     [edi], al           ; fill in pixels
  984.     add     edi, ebx            ; point to next line (below)
  985.     loopx   cx, @fb_one_loop    ; loop until left strip is drawn
  986.  
  987. @fb_exit:
  988.     pop     edi esi ebp         ; restore saved registers
  989.     ret     10                  ; exit and clean up stack
  990.  
  991.     endif
  992.     if x_draw_line eq 1
  993.  
  994. ;=====================================================
  995. ;draw_line (xpos1%, ypos1%, xpos2%, ypos2%, colornum%)
  996. ;=====================================================
  997. ;
  998. ; draws a line on the active display page
  999. ;
  1000. ; entry: xpos1    = x position of first point on line
  1001. ;        ypos1    = y position of first point on line
  1002. ;        xpos2    = x position of last point on line
  1003. ;        ypos2    = y position of last point on line
  1004. ;        colornum = color to draw line with
  1005. ;
  1006. ; exit:  no meaningful values returned
  1007. ;
  1008.  
  1009. dl_stack    struc
  1010.                 dd  ?x3 ; edi, esi, ebp
  1011.                 dd  ?   ; caller
  1012.     dl_colorf   db  ?,? ; line draw color
  1013.     dl_ypos2    dw  ?   ; y pos of last point
  1014.     dl_xpos2    dw  ?   ; x pos of last point
  1015.     dl_ypos1    dw  ?   ; y pos of first point
  1016.     dl_xpos1    dw  ?   ; x pos of first point
  1017. dl_stack    ends
  1018.  
  1019.         public draw_line
  1020.  
  1021. draw_line:
  1022.  
  1023.     push    ebp esi edi         ; preserve important registers
  1024.     mov     ebp, esp            ; set up stack frame
  1025.     cld                         ; direction flag = forward
  1026.  
  1027.     out_8   sc_index, map_mask  ; set up for plane select
  1028.     mov     ch, [ebp].dl_colorf ; save line color in ch
  1029.  
  1030. ; check line type
  1031.  
  1032.     movzx   esi, [ebp].dl_xpos1 ; ax = x1   is x1< x2?
  1033.     movzx   edi, [ebp].dl_xpos2 ; dx = x2
  1034.     cmp     si, di              ; is x1 < x2
  1035.     je      @dl_vline           ; if x1=x2, draw vertical line
  1036.     jl      @dl_noswap1         ; if x1 < x2, don't swap
  1037.  
  1038.     xchg    si, di              ; x2 is > x1, so swap them
  1039.  
  1040. @dl_noswap1:
  1041.  
  1042. ; si = x1, di = x2
  1043.  
  1044.     mov     ax, [ebp].dl_ypos1  ; ax = y1   is y1 <> y2?
  1045.     cmp     ax, [ebp].dl_ypos2  ; y1 = y2?
  1046.     je      @dl_horz            ; if so, draw a horizontal line
  1047.  
  1048.     jmp     @dl_brezham         ; diagonal line... go do it...
  1049.  
  1050. ; this code draws a horizontal line in mode x where:
  1051. ; si = x1, di = x2, and ax = y1/y2
  1052.  
  1053. @dl_horz:
  1054.  
  1055.     mul     screen_width        ; offset = ypos * screen_width
  1056.     mov     dx, ax              ; cx = line offset into page
  1057.  
  1058.     mov     ax, si                  ; get left edge, save x1
  1059.     and     si, plane_bits          ; mask out row #
  1060.     mov     bl, left_clip_mask[esi] ; get left edge mask
  1061.     mov     cx, di                  ; get right edge, save x2
  1062.     and     di, plane_bits          ; mask out row #
  1063.     mov     bh, right_clip_mask[edi] ; get right edge mask byte
  1064.  
  1065.     shr     ax, 2               ; get x1 byte # (=x1/4)
  1066.     shr     cx, 2               ; get x2 byte # (=x2/4)
  1067.  
  1068.     movzx   eax, ax             ; zero high words for add
  1069.     movzx   edx, dx
  1070.     movzx   ecx, cx
  1071.  
  1072.     mov     edi, current_page   ; point to active vga page
  1073.     add     edi, edx            ; point to start of line
  1074.     add     edi, eax            ; point to pixel x1
  1075.  
  1076.     sub     cx, ax              ; cx = # of bands (-1) to set
  1077.     jnz     @dl_longln          ; jump if longer than one segment
  1078.  
  1079.     and     bl, bh              ; otherwise, merge clip masks
  1080.  
  1081. @dl_longln:
  1082.  
  1083.     out_8   sc_data, bl         ; set the left clip mask
  1084.  
  1085.     mov     al, [ebp].dl_colorf ; get line color
  1086.     mov     bl, al              ; bl = copy of line color
  1087.     stosb                       ; set left (1-4) pixels
  1088.  
  1089.     jcxz    @dl_exit3           ; done if only one line segment
  1090.  
  1091.     dec     cx                  ; cx = # of middle segments
  1092.     jz      @dl_xrseg           ; if no middle segments....
  1093.  
  1094. ; draw middle segments
  1095.  
  1096.     out_8   dx, all_planes      ; write to all planes
  1097.  
  1098.     mov     al, bl              ; get color from bl
  1099.     rep     stosb               ; draw middle (4 pixel) segments
  1100.  
  1101. @dl_xrseg:
  1102.     out_8   dx, bh              ; select planes for right clip mask
  1103.     mov     al, bl              ; get color value
  1104.     stosb                       ; draw right (1-4) pixels
  1105. @dl_exit3:
  1106.     jmp     s @dl_exit          ; we are done...
  1107.  
  1108. ; this code draws a vertical line.  on entry:
  1109. ; ch = line color, si & di = x1
  1110.  
  1111. @dl_vline:
  1112.  
  1113.     mov     ax, [ebp].dl_ypos1  ; ax = y1
  1114.     mov     si, [ebp].dl_ypos2  ; si = y2
  1115.     cmp     ax, si              ; is y1 < y2?
  1116.     jle     @dl_noswap2         ; if so, don't swap them
  1117.  
  1118.     xchg    ax, si              ; ok, now y1 < y2
  1119.  
  1120. @dl_noswap2:
  1121.  
  1122.     sub     si, ax              ; si = line height (y2-y1+1)
  1123.     inc     si
  1124.  
  1125. ; ax = y1, di = x1, get offset into page into ax
  1126.  
  1127.     mul     screen_width        ; offset = y1 (ax) * screen width
  1128.     mov     dx, di              ; copy xpos into dx
  1129.     shr     di, 2               ; di = xpos/4
  1130.     add     ax, di              ; di = xpos/4 + screenwidth * y1
  1131.  
  1132.     movzx   eax, ax
  1133.     mov     edi, current_page   ; point to active vga page
  1134.     add     edi, eax            ; point to pixel x1, y1
  1135.  
  1136. ;select plane
  1137.  
  1138.     mov     cl, dl              ; cl = save x1
  1139.     and     cl, plane_bits      ; get x1 mod 4 (plane #)
  1140.     mov     ax, map_mask_plane1 ; code to set plane #1
  1141.     shl     ah, cl              ; change to correct plane #
  1142.     out_16  sc_index, ax        ; select plane
  1143.  
  1144.     mov     al, ch              ; get saved color
  1145.     mov     bx, screen_width    ; get offset to advance line by
  1146.     movzx   ebx, bx
  1147.  
  1148. @dl_vloop:
  1149.     mov     [edi], al           ; draw single pixel
  1150.     add     edi, ebx            ; point to next line
  1151.     loopjz  si, @dl_exit        ; lines--, exit if done
  1152.  
  1153.     mov     [edi], al           ; draw single pixel
  1154.     add     edi, ebx            ; point to next line
  1155.     loopx   si, @dl_vloop       ; lines--, loop until done
  1156.  
  1157. @dl_exit:
  1158.  
  1159.     jmp     @dl_exit2           ; done!
  1160.  
  1161. ; this code draws a diagonal line in mode x
  1162.  
  1163. @dl_brezham:
  1164.     mov     edi, current_page   ; point to active vga page
  1165.  
  1166.     mov     ax, [ebp].dl_ypos1  ; get y1 value
  1167.     mov     bx, [ebp].dl_ypos2  ; get y2 value
  1168.     mov     cx, [ebp].dl_xpos1  ; get starting xpos
  1169.  
  1170.     cmp     bx, ax              ; y2-y1 is?
  1171.     jnc     @dl_deltayok        ; if y2>=y1 then goto...
  1172.  
  1173.     xchg    bx, ax              ; swap em...
  1174.     mov     cx, [ebp].dl_xpos2  ; get new starting xpos
  1175.  
  1176. @dl_deltayok:
  1177.     mul     screen_width        ; offset = screen_width * y1
  1178.     movzx   eax, ax
  1179.  
  1180.     add     edi, eax            ; di -> start of line y1 on page
  1181.     mov     ax, cx              ; ax = xpos (x1)
  1182.     shr     ax, 2               ; /4 = byte offset into line
  1183.     add     edi, eax            ; di = starting pos (x1,y1)
  1184.  
  1185.     mov     al, 11h             ; staring mask
  1186.     and     cl, plane_bits      ; get plane #
  1187.     shl     al, cl              ; and shift into place
  1188.     mov     ah, [ebp].dl_colorf ; color in hi bytes
  1189.  
  1190.     push    ax                  ; save mask,color...
  1191.  
  1192.     mov     ah, al              ; plane # in ah
  1193.     mov     al, map_mask        ; select plane register
  1194.     out_16  sc_index, ax        ; select initial plane
  1195.  
  1196.     mov     ax, [ebp].dl_xpos1  ; get x1 value
  1197.     mov     bx, [ebp].dl_ypos1  ; get y1 value
  1198.     mov     cx, [ebp].dl_xpos2  ; get x2 value
  1199.     mov     dx, [ebp].dl_ypos2  ; get y2 value
  1200.  
  1201.     movzx   ebp, screen_width   ; use bp for line width to
  1202.                                 ; to avoid extra memory access
  1203.  
  1204.     sub     dx, bx              ; figure delta_y
  1205.     jnc     @dl_deltayok2       ; jump if y2 >= y1
  1206.  
  1207.     add     bx, dx              ; put y2 into y1
  1208.     neg     dx                  ; abs(delta_y)
  1209.     xchg    ax, cx              ; and exchange x1 and x2
  1210.  
  1211. @dl_deltayok2:
  1212.     mov     bx, 08000h          ; seed for fraction accumulator
  1213.  
  1214.     sub     cx, ax              ; figure delta_x
  1215.     jc      @dl_drawleft        ; if negative, go left
  1216.  
  1217.     jmp     @dl_drawright       ; draw line that slopes right
  1218.  
  1219. @dl_drawleft:
  1220.  
  1221.     neg     cx                  ; abs(delta_x)
  1222.  
  1223.     cmp     cx, dx              ; is delta_x < delta_y?
  1224.     jb      @dl_steepleft       ; yes, so go do steep line
  1225.                                 ; (delta_y iterations)
  1226.  
  1227. ; draw a shallow line to the left in mode x
  1228.  
  1229. @dl_shallowleft:
  1230.     clr     ax                  ; zero low word of delta_y * 10000h
  1231.     sub     ax, dx              ; dx:ax <- dx * 0ffffh
  1232.     sbb     dx, 0               ; include carry
  1233.     div     cx                  ; divide by delta_x
  1234.  
  1235.     mov     si, bx              ; si = accumulator
  1236.     mov     bx, ax              ; bx = add fraction
  1237.     pop     ax                  ; get color, bit mask
  1238.     mov     dx, sc_data         ; sequence controller data register
  1239.     inc     cx                  ; inc delta_x so we can unroll loop
  1240.  
  1241. ; loop (x2) to draw pixels, move left, and maybe down...
  1242.  
  1243. @dl_sllloop:
  1244.     mov     [edi], ah           ; set first pixel, plane data set up
  1245.     loopjz  cx, @dl_sllexit     ; delta_x--, exit if done
  1246.  
  1247.     add     si, bx              ; add numerator to accumulator
  1248.     jnc     @dl_slll2nc         ; move down on carry
  1249.  
  1250.     add     edi, ebp            ; move down one line...
  1251.  
  1252. @dl_slll2nc:
  1253.     dec     edi                 ; left one addr
  1254.     ror     al, 1               ; move left one plane, back on 0 1 2
  1255.     cmp     al, 87h             ; wrap?, if al <88 then carry set
  1256.     adc     edi, 0              ; adjust address: di = di + carry
  1257.     out     dx, al              ; set up new bit plane mask
  1258.  
  1259.     mov     [edi], ah           ; set pixel
  1260.     loopjz  cx, @dl_sllexit     ; delta_x--, exit if done
  1261.  
  1262.     add     si, bx              ; add numerator to accumulator,
  1263.     jnc     @dl_slll3nc         ; move down on carry
  1264.  
  1265.     add     edi, ebp            ; move down one line...
  1266.  
  1267. @dl_slll3nc:                    ; now move left a pixel...
  1268.     dec     edi                 ; left one addr
  1269.     ror     al, 1               ; move left one plane, back on 0 1 2
  1270.     cmp     al, 87h             ; wrap?, if al <88 then carry set
  1271.     adc     edi, 0              ; adjust address: di = di + carry
  1272.     out     dx, al              ; set up new bit plane mask
  1273.     jmp     s @dl_sllloop       ; loop until done
  1274.  
  1275. @dl_sllexit:
  1276.     jmp     @dl_exit2           ; and exit
  1277.  
  1278. ; draw a steep line to the left in mode x
  1279.  
  1280. @dl_steepleft:
  1281.     clr     ax                  ; zero low word of delta_y * 10000h
  1282.     xchg    dx, cx              ; delta_y switched with delta_x
  1283.     div     cx                  ; divide by delta_y
  1284.  
  1285.     mov     si, bx              ; si = accumulator
  1286.     mov     bx, ax              ; bx = add fraction
  1287.     pop     ax                  ; get color, bit mask
  1288.     mov     dx, sc_data         ; sequence controller data register
  1289.     inc     cx                  ; inc delta_y so we can unroll loop
  1290.  
  1291. ; loop (x2) to draw pixels, move down, and maybe left
  1292.  
  1293. @dl_stlloop:
  1294.  
  1295.     mov     [edi], ah           ; set first pixel
  1296.     loopjz  cx, @dl_stlexit     ; delta_y--, exit if done
  1297.  
  1298.     add     si, bx              ; add numerator to accumulator
  1299.     jnc     @dl_stlnc2          ; no carry, just move down!
  1300.  
  1301.     dec     edi                 ; move left one addr
  1302.     ror     al, 1               ; move left one plane, back on 0 1 2
  1303.     cmp     al, 87h             ; wrap?, if al <88 then carry set
  1304.     adc     edi, 0              ; adjust address: di = di + carry
  1305.     out     dx, al              ; set up new bit plane mask
  1306.  
  1307. @dl_stlnc2:
  1308.     add     edi, ebp            ; advance to next line.
  1309.  
  1310.     mov     [edi], ah           ; set pixel
  1311.     loopjz  cx, @dl_stlexit     ; delta_y--, exit if done
  1312.  
  1313.     add     si, bx              ; add numerator to accumulator
  1314.     jnc     @dl_stlnc3          ; no carry, just move down!
  1315.  
  1316.     dec     edi                 ; move left one addr
  1317.     ror     al, 1               ; move left one plane, back on 0 1 2
  1318.     cmp     al, 87h             ; wrap?, if al <88 then carry set
  1319.     adc     edi, 0              ; adjust address: di = di + carry
  1320.     out     dx, al              ; set up new bit plane mask
  1321.  
  1322. @dl_stlnc3:
  1323.     add     edi, ebp            ; advance to next line.
  1324.     jmp     s @dl_stlloop       ; loop until done
  1325.  
  1326. @dl_stlexit:
  1327.     jmp     @dl_exit2           ; and exit
  1328.  
  1329. ; draw a line that goes to the right...
  1330.  
  1331. @dl_drawright:
  1332.     cmp     cx, dx              ; is delta_x < delta_y?
  1333.     jb      @dl_steepright      ; yes, so go do steep line
  1334.                                 ; (delta_y iterations)
  1335.  
  1336. ; draw a shallow line to the right in mode x
  1337.  
  1338. @dl_shallowright:
  1339.     clr     ax                  ; zero low word of delta_y * 10000h
  1340.     sub     ax, dx              ; dx:ax <- dx * 0ffffh
  1341.     sbb     dx, 0               ; include carry
  1342.     div     cx                  ; divide by delta_x
  1343.  
  1344.     mov     si, bx              ; si = accumulator
  1345.     mov     bx, ax              ; bx = add fraction
  1346.     pop     ax                  ; get color, bit mask
  1347.     mov     dx, sc_data         ; sequence controller data register
  1348.     inc     cx                  ; inc delta_x so we can unroll loop
  1349.  
  1350. ; loop (x2) to draw pixels, move right, and maybe down...
  1351.  
  1352. @dl_slrloop:
  1353.     mov     [edi], ah           ; set first pixel, mask is set up
  1354.     loopjz  cx, @dl_slrexit     ; delta_x--, exit if done..
  1355.  
  1356.     add     si, bx              ; add numerator to accumulator
  1357.     jnc     @dl_slr2nc          ; don't move down if carry not set
  1358.  
  1359.     add     edi, ebp            ; move down one line...
  1360.  
  1361. @dl_slr2nc:                     ; now move right a pixel...
  1362.     rol     al, 1               ; move right one addr if plane = 0
  1363.     cmp     al, 12h             ; wrap? if al >12 then carry not set
  1364.     adc     edi, 0              ; adjust address: di = di + carry
  1365.     out     dx, al              ; set up new bit plane mask
  1366.  
  1367.     mov     [edi], ah           ; set pixel
  1368.     loopjz  cx, @dl_slrexit     ; delta_x--, exit if done..
  1369.  
  1370.     add     si, bx              ; add numerator to accumulator
  1371.     jnc     @dl_slr3nc          ; don't move down if carry not set
  1372.  
  1373.     add     edi, ebp            ; move down one line...
  1374.  
  1375. @dl_slr3nc:
  1376.     rol     al, 1               ; move right one addr if plane = 0
  1377.     cmp     al, 12h             ; wrap? if al >12 then carry not set
  1378.     adc     edi, 0              ; adjust address: di = di + carry
  1379.     out     dx, al              ; set up new bit plane mask
  1380.     jmp     s @dl_slrloop       ; loop till done
  1381.  
  1382. @dl_slrexit:
  1383.     jmp     @dl_exit2           ; and exit
  1384.  
  1385. ; draw a steep line to the right in mode x
  1386.  
  1387. @dl_steepright:
  1388.     clr     ax                  ; zero low word of delta_y * 10000h
  1389.     xchg    dx, cx              ; delta_y switched with delta_x
  1390.     div     cx                  ; divide by delta_y
  1391.  
  1392.     mov     si, bx              ; si = accumulator
  1393.     mov     bx, ax              ; bx = add fraction
  1394.     pop     ax                  ; get color, bit mask
  1395.     mov     dx, sc_data         ; sequence controller data register
  1396.     inc     cx                  ; inc delta_y so we can unroll loop
  1397.  
  1398. ; loop (x2) to draw pixels, move down, and maybe right
  1399.  
  1400. @strloop:
  1401.     mov     [edi], ah           ; set first pixel, mask is set up
  1402.     loopjz  cx, @dl_exit2       ; delta_y--, exit if done
  1403.  
  1404.     add     si, bx              ; add numerator to accumulator
  1405.     jnc     @strnc2             ; if no carry then just go down...
  1406.  
  1407.     rol     al, 1               ; move right one addr if plane = 0
  1408.     cmp     al, 12h             ; wrap? if al >12 then carry not set
  1409.     adc     edi, 0              ; adjust address: di = di + carry
  1410.     out     dx, al              ; set up new bit plane mask
  1411.  
  1412. @strnc2:
  1413.     add     edi, ebp            ; advance to next line.
  1414.  
  1415.     mov     [edi], ah           ; set pixel
  1416.     loopjz  cx, @dl_exit2       ; delta_y--, exit if done
  1417.  
  1418.     add     si, bx              ; add numerator to accumulator
  1419.     jnc     @strnc3             ; if no carry then just go down...
  1420.  
  1421.     rol     al, 1               ; move right one addr if plane = 0
  1422.     cmp     al, 12h             ; wrap? if al >12 then carry not set
  1423.     adc     edi, 0              ; adjust address: di = di + carry
  1424.     out     dx, al              ; set up new bit plane mask
  1425.  
  1426. @strnc3:
  1427.     add     edi, ebp            ; advance to next line.
  1428.     jmp     s @strloop          ; loop till done
  1429.  
  1430. @dl_exit2:
  1431.     pop     edi esi ebp         ; restore saved registers
  1432.     ret     10                  ; exit and clean up stack
  1433.  
  1434.     endif
  1435.  
  1436. ; ===== dac color register routines =====
  1437.  
  1438. ;=================================================
  1439. ;set_dac_register (register%, red%, green%, blue%)
  1440. ;=================================================
  1441. ;
  1442. ; sets a single (rgb) vga palette register
  1443. ;
  1444. ; entry: register = the dac # to modify (0-255)
  1445. ;        red      = the new red intensity (0-63)
  1446. ;        green    = the new green intensity (0-63)
  1447. ;        blue     = the new blue intensity (0-63)
  1448. ;
  1449. ; exit:  no meaningful values returned
  1450. ;
  1451.  
  1452. sdr_stack   struc
  1453.                     dd  ?   ; ebp
  1454.                     dd  ?   ; caller
  1455.     sdr_blue        db  ?,? ; blue data value
  1456.     sdr_green       db  ?,? ; green data value
  1457.     sdr_red         db  ?,? ; red data value
  1458.     sdr_register    db  ?,? ; palette register #
  1459. sdr_stack   ends
  1460.  
  1461.     public  set_dac_register
  1462.  
  1463. set_dac_register:
  1464.  
  1465.     push    ebp                 ; save bp
  1466.     mov     ebp, esp            ; set up stack frame
  1467.  
  1468. ; select which dac register to modify
  1469.  
  1470.     out_8   dac_write_addr, [ebp].sdr_register
  1471.  
  1472.     mov     dx, pel_data_reg    ; dac data register
  1473.     out_8   dx, [ebp].sdr_red   ; set red intensity
  1474.     out_8   dx, [ebp].sdr_green ; set green intensity
  1475.     out_8   dx, [ebp].sdr_blue  ; set blue intensity
  1476.  
  1477.     pop     ebp                 ; restore registers
  1478.     ret     8                   ; exit & clean up stack
  1479.  
  1480. ;====================================================
  1481. ;get_dac_register (register%, &red%, &green%, &blue%)
  1482. ;====================================================
  1483. ;
  1484. ; reads the rgb values of a single vga palette register
  1485. ;
  1486. ; entry: register = the dac # to read (0-255)
  1487. ;        red      = offset to red variable in ds
  1488. ;        green    = offset to green variable in ds
  1489. ;        blue     = offset to blue variable in ds
  1490. ;
  1491. ; exit:  the values of the integer variables red,
  1492. ;        green, and blue are set to the values
  1493. ;        taken from the specified dac register.
  1494. ;
  1495.  
  1496. gdr_stack   struc
  1497.                     dd  ?   ; ebp
  1498.                     dd  ?   ; caller
  1499.     gdr_blue        dd  ?   ; addr of blue data value (where to put)
  1500.     gdr_green       dd  ?   ; addr of green data value
  1501.     gdr_red         dd  ?   ; addr of red data value
  1502.     gdr_register    db  ?,? ; palette register #
  1503. gdr_stack   ends
  1504.  
  1505.     public  get_dac_register
  1506.  
  1507. get_dac_register:
  1508.  
  1509.     push    ebp                 ; save bp
  1510.     mov     ebp, esp            ; set up stack frame
  1511.  
  1512. ; select which dac register to read in
  1513.  
  1514.     out_8   dac_read_addr, [ebp].gdr_register
  1515.  
  1516.     mov     dx, pel_data_reg    ; dac data register
  1517.     clr     ax                  ; clear ax
  1518.  
  1519.     in      al, dx              ; read red value
  1520.     mov     ebx, [ebp].gdr_red  ; get address of red%
  1521.     mov     [ebx], ax           ; *red% = ax
  1522.  
  1523.     in      al, dx              ; read green value
  1524.     mov     ebx, [ebp].gdr_green; get address of green%
  1525.     mov     [ebx], ax           ; *green% = ax
  1526.  
  1527.     in      al, dx              ; read blue value
  1528.     mov     ebx, [ebp].gdr_blue ; get address of blue%
  1529.     mov     [ebx], ax           ; *blue% = ax
  1530.  
  1531.     pop     ebp                 ; restore registers
  1532.     ret     14                  ; exit & clean up stack
  1533.  
  1534. ;===========================================================
  1535. ;load_dac_registers (seg paldata, startreg%, endreg%, sync%)
  1536. ;===========================================================
  1537. ;
  1538. ; sets a block of vga palette registers
  1539. ;
  1540. ; entry: paldata  = far pointer to block of palette data
  1541. ;        startreg = first register # in range to set (0-255)
  1542. ;        endreg   = last register # in range to set (0-255)
  1543. ;        sync     = wait for vertical retrace flag (boolean)
  1544. ;
  1545. ; exit:  no meaningful values returned
  1546. ;
  1547. ; notes: paldata is a lifar array of 3 byte palette values
  1548. ;        in the order: red  (0-63), green (0-63), blue (0-63)
  1549. ;
  1550.  
  1551. ldr_stack   struc
  1552.                     dd  ?,? ; ebp, esi
  1553.                     dd  ?   ; caller
  1554.     ldr_sync        dw  ?   ; vertical sync flag
  1555.     ldr_endreg      db  ?,? ; last register #
  1556.     ldr_startreg    db  ?,? ; first register #
  1557.     ldr_paldata     dd  ?   ; far ptr to palette data
  1558. ldr_stack   ends
  1559.  
  1560.     public  load_dac_registers
  1561.  
  1562. load_dac_registers:
  1563.  
  1564.     push    ebp esi             ; save registers
  1565.     mov     ebp, esp            ; set up stack frame
  1566.  
  1567.     mov     ax, [ebp].ldr_sync  ; get vertical sync flag
  1568.     or      ax, ax              ; is sync flag = 0?
  1569.     jz      @ldr_load           ; if so, skip call
  1570.  
  1571.     call    sync_display        ; wait for vsync
  1572.  
  1573. ; determine register #'s, size to copy, etc
  1574.  
  1575. @ldr_load:
  1576.  
  1577.     mov     esi, [ebp].ldr_paldata  ; esi -> palette data
  1578.     mov     dx, dac_write_addr      ; dac register # selector
  1579.  
  1580.     clr     ax, bx                  ; clear for byte loads
  1581.     mov     al, [ebp].ldr_startreg  ; get start register
  1582.     mov     bl, [ebp].ldr_endreg    ; get end register
  1583.  
  1584.     sub     bx, ax              ; bx = # of dac registers -1
  1585.     inc     bx                  ; bx = # of dac registers
  1586.     mov     cx, bx              ; cx = # of dac registers
  1587.     add     cx, bx              ; cx =  "   " * 2
  1588.     add     cx, bx              ; cx =  "   " * 3
  1589.     cld                         ; block outs forward
  1590.     out     dx, al              ; set up correct register #
  1591.  
  1592. ; load a block of dac registers
  1593.  
  1594.     mov     dx, pel_data_reg    ; dac data register
  1595.     movzx   ecx,cx
  1596.  
  1597.     rep     outsb               ; block set dac registers
  1598.  
  1599.     pop     esi ebp             ; restore registers
  1600.     ret     10                  ; exit & clean up stack
  1601.  
  1602. ;====================================================
  1603. ;read_dac_registers (seg paldata, startreg%, endreg%)
  1604. ;====================================================
  1605. ;
  1606. ; reads a block of vga palette registers
  1607. ;
  1608. ; entry: paldata  = far pointer to block to store palette data
  1609. ;        startreg = first register # in range to read (0-255)
  1610. ;        endreg   = last register # in range to read (0-255)
  1611. ;
  1612. ; exit:  no meaningful values returned
  1613. ;
  1614. ; notes: paldata is a lifar array of 3 byte palette values
  1615. ;        in the order: red  (0-63), green (0-63), blue (0-63)
  1616. ;
  1617.  
  1618. rdr_stack   struc
  1619.                     dd  ?,? ; ebp, edi
  1620.                     dd  ?   ; caller
  1621.     rdr_endreg      db  ?,? ; last register #
  1622.     rdr_startreg    db  ?,? ; first register #
  1623.     rdr_paldata     dd  ?   ; far ptr to palette data
  1624. rdr_stack   ends
  1625.  
  1626.     public  read_dac_registers
  1627.  
  1628. read_dac_registers:
  1629.  
  1630.     push    ebp edi             ; save registers
  1631.     mov     ebp, esp            ; set up stack frame
  1632.  
  1633. ; determine register #'s, size to copy, etc
  1634.  
  1635.     mov     edi, [ebp].rdr_paldata  ; edi -> palette buffer
  1636.     mov     dx, dac_read_addr       ; dac register # selector
  1637.  
  1638.     clr     ax, bx                  ; clear for byte loads
  1639.     mov     al, [ebp].rdr_startreg  ; get start register
  1640.     mov     bl, [ebp].rdr_endreg    ; get end register
  1641.  
  1642.     sub     bx, ax              ; bx = # of dac registers -1
  1643.     inc     bx                  ; bx = # of dac registers
  1644.     mov     cx, bx              ; cx = # of dac registers
  1645.     add     cx, bx              ; cx =  "   " * 2
  1646.     add     cx, bx              ; cx =  "   " * 3
  1647.     cld                         ; block ins forward
  1648.  
  1649. ; read a block of dac registers
  1650.  
  1651.     out     dx, al              ; set up correct register #
  1652.     mov     dx, pel_data_reg    ; dac data register
  1653.     movzx   ecx,cx
  1654.  
  1655.     rep     insb                ; block read dac registers
  1656.  
  1657.     pop     edi ebp             ; restore registers
  1658.     ret     8                   ; exit & clean up stack
  1659.  
  1660. ; ===== page flipping and scrolling routines =====
  1661.  
  1662. ;=========================
  1663. ;set_active_page (pageno%)
  1664. ;=========================
  1665. ;
  1666. ; sets the active display page to be used for future drawing
  1667. ;
  1668. ; entry: pageno = display page to make active
  1669. ;        (values: 0 to number of pages - 1)
  1670. ;
  1671. ; exit:  no meaningful values returned
  1672. ;
  1673.  
  1674. sap_stack   struc
  1675.                 dd  ?   ; ebp
  1676.                 dd  ?   ; caller
  1677.     sap_page    dw  ?   ; page # for drawing
  1678. sap_stack   ends
  1679.  
  1680.     public  set_active_page
  1681.  
  1682. set_active_page:
  1683.  
  1684.     push    ebp                 ; preserve registers
  1685.     mov     ebp, esp            ; set up stack frame
  1686.  
  1687.     movzx   ebx, [ebp].sap_page ; get desired page #
  1688.     cmp     bx, last_page       ; is page # valid?
  1689.     jae     @sap_exit           ; if not, do nothing
  1690.  
  1691.     mov     active_page, bx     ; set active page #
  1692.  
  1693.     shl     bx, 2               ; scale page # to dword
  1694.     mov     eax, page_addr[ebx] ; get offset to page
  1695.  
  1696.     mov     current_page, eax   ; and set for future mov's
  1697.  
  1698. @sap_exit:
  1699.     pop     ebp                 ; restore registers
  1700.     ret     2                   ; exit and clean up stack
  1701.  
  1702. ;================
  1703. ;get_active_page%
  1704. ;================
  1705. ;
  1706. ; returns the video page # currently used for drawing
  1707. ;
  1708. ; entry: no parameters are passed
  1709. ;
  1710. ; exit:  ax = current video page used for drawing
  1711. ;
  1712.  
  1713.     public  get_active_page
  1714.  
  1715. get_active_page:
  1716.  
  1717.     mov     ax, active_page     ; get active page #
  1718.     ret                         ; exit and clean up stack
  1719.  
  1720. ;===============================
  1721. ;set_display_page (displaypage%)
  1722. ;===============================
  1723. ;
  1724. ; sets the currently visible display page.
  1725. ; when called this routine syncronizes the display
  1726. ; to the vertical blank.
  1727. ;
  1728. ; entry: pageno = display page to show on the screen
  1729. ;        (values: 0 to number of pages - 1)
  1730. ;
  1731. ; exit:  no meaningful values returned
  1732. ;
  1733.  
  1734. sdp_stack   struc
  1735.                 dd  ?       ; ebp
  1736.                 dd  ?       ; caller
  1737.     sdp_page    dw  ?       ; page # to display...
  1738. sdp_stack   ends
  1739.  
  1740.     public  set_display_page
  1741.  
  1742. set_display_page:
  1743.  
  1744.     push    ebp                 ; preserve registers
  1745.     mov     ebp, esp            ; set up stack frame
  1746.  
  1747.     movzx   ebx, [ebp].sdp_page ; get desired page #
  1748.     cmp     bx, last_page       ; is page # valid?
  1749.     jae     @sdp_exit           ; if not, do nothing
  1750.  
  1751.     mov     display_page, bx    ; set display page #
  1752.  
  1753.     shl     bx, 2               ; scale page # to dword
  1754.     mov     ecx, page_addr[ebx] ; get offset in memory to page
  1755.     add     ecx, current_moffset ; adjust for any scrolling
  1756.     add     ecx,_code32a        ; adjust for protected mode
  1757.  
  1758. ; wait if we are currently in a vertical retrace
  1759.  
  1760.     mov     dx, input_1         ; input status #1 register
  1761.  
  1762. @dp_wait0:
  1763.     in      al, dx              ; get vga status
  1764.     and     al, vert_retrace    ; in display mode yet?
  1765.     jnz     @dp_wait0           ; if not, wait for it
  1766.  
  1767. ; set the start display address to the new page
  1768.  
  1769.     mov     dx, crtc_index      ; we change the vga sequencer
  1770.  
  1771.     mov     al, start_disp_lo   ; display start low register
  1772.     mov     ah, cl              ; low 8 bits of start addr
  1773.     out     dx, ax              ; set display addr low
  1774.  
  1775.     mov     al, start_disp_hi   ; display start high register
  1776.     mov     ah, ch              ; high 8 bits of start addr
  1777.     out     dx, ax              ; set display addr high
  1778.  
  1779. ; wait for a vertical retrace to smooth out things
  1780.  
  1781.     mov     dx, input_1         ; input status #1 register
  1782.  
  1783. @dp_wait1:
  1784.     in      al, dx              ; get vga status
  1785.     and     al, vert_retrace    ; vertical retrace start?
  1786.     jz      @dp_wait1           ; if not, wait for it
  1787.  
  1788. @sdp_exit:
  1789.     pop     ebp                 ; restore registers
  1790.     ret     2                   ; exit and clean up stack
  1791.  
  1792. ;=================
  1793. ;get_display_page%
  1794. ;=================
  1795. ;
  1796. ; returns the video page # currently displayed
  1797. ;
  1798. ; entry: no parameters are passed
  1799. ;
  1800. ; exit:  ax = current video page being displayed
  1801. ;
  1802.  
  1803.     public  get_display_page
  1804.  
  1805. get_display_page:
  1806.  
  1807.     mov     ax, display_page    ; get display page #
  1808.     ret                         ; exit & clean up stack
  1809.  
  1810.     if x_set_window eq 1
  1811.  
  1812. ;=======================================
  1813. ;set_window (displaypage%, xpos%, ypos%)
  1814. ;=======================================
  1815. ;
  1816. ; since a logical screen can be larger than the physical
  1817. ; screen, scrolling is possible.  this routine sets the
  1818. ; upper left corner of the screen to the specified pixel.
  1819. ; also sets the display page to simplify combined page
  1820. ; flipping and scrolling.  when called this routine
  1821. ; syncronizes the display to the vertical blank.
  1822. ;
  1823. ; entry: displaypage = display page to show on the screen
  1824. ;        xpos        = # of pixels to shift screen right
  1825. ;        ypos        = # of lines to shift screen down
  1826. ;
  1827. ; exit:  no meaningful values returned
  1828. ;
  1829.  
  1830. sw_stack    struc
  1831.                 dd  ?   ; ebp
  1832.                 dd  ?   ; caller
  1833.     sw_ypos     dw  ?   ; y pos of ul screen corner
  1834.     sw_xpos     dw  ?   ; x pos of ul screen corner
  1835.     sw_page     dw  ?   ; (new) display page
  1836. sw_stack    ends
  1837.  
  1838.         public set_window
  1839.  
  1840. set_window:
  1841.  
  1842.     push    ebp                 ; preserve registers
  1843.     mov     ebp, esp            ; set up stack frame
  1844.  
  1845. ; check if our scroll offsets are valid
  1846.  
  1847.     mov     bx, [ebp].sw_page   ; get desired page #
  1848.     cmp     bx, last_page       ; is page # valid?
  1849.     jae     @sw_exit            ; if not, do nothing
  1850.  
  1851.     mov     ax, [ebp].sw_ypos   ; get desired y offset
  1852.     cmp     ax, max_yoffset     ; is it within limits?
  1853.     ja      @sw_exit            ; if not, exit
  1854.  
  1855.     mov     cx, [ebp].sw_xpos   ; get desired x offset
  1856.     cmp     cx, max_xoffset     ; is it within limits?
  1857.     ja      @sw_exit            ; if not, exit
  1858.  
  1859. ; compute proper display start address to use
  1860.  
  1861.     mul     screen_width        ; ax = yoffset * line width
  1862.     shr     cx, 2               ; cx / 4 = bytes into line
  1863.     add     ax, cx              ; ax = offset of upper left pixel
  1864.     movzx   eax, ax
  1865.     movzx   ebx, bx
  1866.  
  1867.     mov     current_moffset, eax ; save offset info
  1868.  
  1869.     mov     display_page, bx    ; set current page #
  1870.     shl     bx, 2               ; scale page # to dword
  1871.     add     eax, page_addr[ebx] ; get offset in vga to page
  1872.     add     eax,_code32a        ; adjust for protected mode segment
  1873.     mov     bx, ax              ; bx = desired display start
  1874.  
  1875.     mov     dx, input_1         ; input status #1 register
  1876.  
  1877. ; wait if we are currently in a vertical retrace
  1878.  
  1879. @sw_wait0:
  1880.     in      al, dx              ; get vga status
  1881.     and     al, vert_retrace    ; in display mode yet?
  1882.     jnz     @sw_wait0           ; if not, wait for it
  1883.  
  1884. ; set the start display address to the new window
  1885.  
  1886.     mov     dx, crtc_index      ; we change the vga sequencer
  1887.     mov     al, start_disp_lo   ; display start low register
  1888.     mov     ah, bl              ; low 8 bits of start addr
  1889.     out     dx, ax              ; set display addr low
  1890.  
  1891.     mov     al, start_disp_hi   ; display start high register
  1892.     mov     ah, bh              ; high 8 bits of start addr
  1893.     out     dx, ax              ; set display addr high
  1894.  
  1895. ; wait for a vertical retrace to smooth out things
  1896.  
  1897.     mov     dx, input_1         ; input status #1 register
  1898.  
  1899. @sw_wait1:
  1900.     in      al, dx              ; get vga status
  1901.     and     al, vert_retrace    ; vertical retrace start?
  1902.     jz      @sw_wait1           ; if not, wait for it
  1903.  
  1904. ; now set the horizontal pixel pan values
  1905.  
  1906.     out_8   attrib_ctrl, pixel_pan_reg  ; select pixel pan register
  1907.  
  1908.     mov     ax, [ebp].sw_xpos   ; get desired x offset
  1909.     and     al, 03              ; get # of pixels to pan (0-3)
  1910.     shl     al, 1               ; shift for 256 color mode
  1911.     out     dx, al              ; fine tune the display!
  1912.  
  1913. @sw_exit:
  1914.     pop     ebp                 ; restore saved registers
  1915.     ret     6                   ; exit and clean up stack
  1916.  
  1917. ;=============
  1918. ;get_x_offset%
  1919. ;=============
  1920. ;
  1921. ; returns the x coordinate of the pixel currently display
  1922. ; in the upper left corner of the display
  1923. ;
  1924. ; entry: no parameters are passed
  1925. ;
  1926. ; exit:  ax = current horizontal scroll offset
  1927. ;
  1928.  
  1929.     public  get_x_offset
  1930.  
  1931. get_x_offset:
  1932.  
  1933.     mov     ax, current_xoffset ; get current horz offset
  1934.     ret                         ; exit & clean up stack
  1935.  
  1936. ;=============
  1937. ;get_y_offset%
  1938. ;=============
  1939. ;
  1940. ; returns the y coordinate of the pixel currently display
  1941. ; in the upper left corner of the display
  1942. ;
  1943. ; entry: no parameters are passed
  1944. ;
  1945. ; exit:  ax = current vertical scroll offset
  1946. ;
  1947.  
  1948.     public  get_y_offset
  1949.  
  1950. get_y_offset:
  1951.  
  1952.     mov     ax, current_yoffset ; get current vertical offset
  1953.     ret                         ; exit & clean up stack
  1954.  
  1955.     endif
  1956.  
  1957. ;============
  1958. ;sync_display
  1959. ;============
  1960. ;
  1961. ; pauses the computer until the next vertical retrace starts
  1962. ;
  1963. ; entry: no parameters are passed
  1964. ;
  1965. ; exit:  no meaningful values returned
  1966. ;
  1967.  
  1968.     public  sync_display
  1969.  
  1970. sync_display:
  1971.  
  1972.     mov     dx, input_1         ; input status #1 register
  1973.  
  1974. ; wait for any current retrace to end
  1975.  
  1976. @sd_wait0:
  1977.     in      al, dx              ; get vga status
  1978.     and     al, vert_retrace    ; in display mode yet?
  1979.     jnz     @sd_wait0           ; if not, wait for it
  1980.  
  1981. ; wait for the start of the next vertical retrace
  1982.  
  1983. @sd_wait1:
  1984.     in      al, dx              ; get vga status
  1985.     and     al, vert_retrace    ; vertical retrace start?
  1986.     jz      @sd_wait1           ; if not, wait for it
  1987.  
  1988.     ret
  1989.  
  1990. ; ===== text display routines =====
  1991.  
  1992.     if x_gprintc eq 1
  1993.  
  1994. ;==================================================
  1995. ;gprintc (charnum%, xpos%, ypos%, colorf%, colorb%)
  1996. ;==================================================
  1997. ;
  1998. ; draws an ascii text character using the currently selected
  1999. ; 8x8 font on the active display page.  it would be a simple
  2000. ; exercise to make this routine process variable height fonts.
  2001. ;
  2002. ; entry: charnum = ascii character # to draw
  2003. ;        xpos    = x position to draw character at
  2004. ;        ypos    = y position of to draw character at
  2005. ;        colorf  = color to draw text character in
  2006. ;        colorb  = color to set background to
  2007. ;
  2008. ; exit:  no meaningful values returned
  2009. ;
  2010.  
  2011. gpc_stack   struc
  2012.     gpc_width   dd  ?   ; screen width-1
  2013.     gpc_lines   db  ?,? ; scan lines to decode
  2014.     gpc_t_sets  dd  ?   ; saved charset segment
  2015.                 dd  ?x3 ; edi, esi, ebp
  2016.                 dd  ?   ; caller
  2017.     gpc_colorb  db  ?,? ; background color
  2018.     gpc_colorf  db  ?,? ; text color
  2019.     gpc_ypos    dw  ?   ; y position to print at
  2020.     gpc_xpos    dw  ?   ; x position to print at
  2021.     gpc_char    db  ?,? ; character to print
  2022. gpc_stack   ends
  2023.  
  2024.         public gprintc
  2025.  
  2026. gprintc:
  2027.  
  2028.     push    ebp esi edi         ; preserve important registers
  2029.     sub     esp, 10             ; allocate workspace on stack
  2030.     mov     ebp, esp            ; set up stack frame
  2031.  
  2032.     mov     edi, current_page   ; point to active vga page
  2033.  
  2034.     movzx   eax, screen_width   ; get logical line width
  2035.     mov     ebx, eax            ; bx = screen width
  2036.     dec     bx                  ;    = screen width-1
  2037.     mov     [ebp].gpc_width,ebx ; save for later use
  2038.  
  2039.     mul     [ebp].gpc_ypos      ; start of line = ypos * width
  2040.     add     edi, eax            ; di -> start of line ypos
  2041.  
  2042.     movzx   eax, [ebp].gpc_xpos ; get xpos of character
  2043.     mov     cx, ax              ; save copy of xpos
  2044.     shr     ax, 2               ; bytes into line = xpos/4
  2045.     add     edi, eax            ; di -> (xpos, ypos)
  2046.  
  2047. ;get source addr of character bit map  & save
  2048.  
  2049.     mov     al, [ebp].gpc_char  ; get character #
  2050.     test    al, 080h            ; is hi bit set?
  2051.     jz      @gpc_lowchar        ; nope, use low char set ptr
  2052.  
  2053.     mov     ebx, charset_hi     ; bx = char set ptr:offset
  2054.     jmp     s @gpc_set_char     ; go setup character ptr
  2055.  
  2056. @gpc_lowchar:
  2057.  
  2058.     mov     ebx, charset_low    ; bx = char set ptr:offset
  2059.  
  2060. @gpc_set_char:
  2061.     and     eax, 07fh           ; mask out hi bits
  2062.     shl     ax, 3               ; * 8 bytes per bitmap
  2063.     add     ebx, eax            ; bx = offset of selected char
  2064.     mov     [ebp].gpc_t_sets, ebx ; save segment on stack
  2065.  
  2066.     and     cx, plane_bits      ; get plane #
  2067.     mov     ch, all_planes      ; get initial plane mask
  2068.     shl     ch, cl              ; and shift into position
  2069.     and     ch, all_planes      ; and mask to lower nibble
  2070.  
  2071.     mov     al, 04              ; 4-plane # = # of initial
  2072.     sub     al, cl              ; shifts to align bit mask
  2073.     mov     cl, al              ; shift count for shl
  2074.  
  2075. ;get segment of character map
  2076.  
  2077.     out_8   sc_index, map_mask  ; setup plane selections
  2078.     inc     dx                  ; dx -> sc_data
  2079.  
  2080.     mov     al, 08              ; 8 lines to process
  2081.     mov     [ebp].gpc_lines, al ; save on stack
  2082.  
  2083. @gpc_decode_char_byte:
  2084.  
  2085.     mov     esi, [ebp].gpc_t_sets ; get esi = string
  2086.  
  2087.     mov     bh, [esi]           ; get bit map
  2088.     inc     esi                 ; point to next line
  2089.     mov     [ebp].gpc_t_sets, esi ; and save new pointer...
  2090.  
  2091.     clr     eax                 ; clear ax
  2092.  
  2093.     clr     bl                      ; clear bl
  2094.     rol     bx, cl                  ; bl holds left edge bits
  2095.     movzx   esi, bx                 ; use as table index
  2096.     and     si, char_bits           ; get low bits
  2097.     mov     al, char_plane_data[esi] ; get mask in al
  2098.     jz      @gpc_no_left1bits       ; skip if no pixels to set
  2099.  
  2100.     mov     ah, [ebp].gpc_colorf ; get foreground color
  2101.     out     dx, al               ; set up screen mask
  2102.     mov     [edi], ah            ; write foreground color
  2103.  
  2104. @gpc_no_left1bits:
  2105.     xor     al, ch               ; invert mask for background
  2106.     jz      @gpc_no_left0bits    ; hey, no need for this
  2107.  
  2108.     mov     ah, [ebp].gpc_colorb ; get background color
  2109.     out     dx, al               ; set up screen mask
  2110.     mov     [edi], ah            ; write foreground color
  2111.  
  2112. ;now do middle/last band
  2113.  
  2114. @gpc_no_left0bits:
  2115.     inc     edi                 ; point to next byte
  2116.     rol     bx, 4               ; shift 4 bits
  2117.  
  2118.     movzx   esi, bx                 ; make lookup pointer
  2119.     and     si, char_bits           ; get low bits
  2120.     mov     al, char_plane_data[esi] ; get mask in al
  2121.     jz      @gpc_no_middle1bits     ; skip if no pixels to set
  2122.  
  2123.     mov     ah, [ebp].gpc_colorf ; get foreground color
  2124.     out     dx, al               ; set up screen mask
  2125.     mov     [edi], ah            ; write foreground color
  2126.  
  2127. @gpc_no_middle1bits:
  2128.     xor     al, all_planes       ; invert mask for background
  2129.     jz      @gpc_no_middle0bits  ; hey, no need for this
  2130.  
  2131.     mov     ah, [ebp].gpc_colorb ; get background color
  2132.     out     dx, al               ; set up screen mask
  2133.     mov     [edi], ah            ; write foreground color
  2134.  
  2135. @gpc_no_middle0bits:
  2136.     xor     ch, all_planes      ; invert clip mask
  2137.     cmp     cl, 4               ; aligned by 4?
  2138.     jz      @gpc_next_line      ; if so, exit now..
  2139.  
  2140.     inc     edi                 ; point to next byte
  2141.     rol     bx, 4               ; shift 4 bits
  2142.  
  2143.     movzx   esi, bx                 ; make lookup pointer
  2144.     and     si, char_bits           ; get low bits
  2145.     mov     al, char_plane_data[esi] ; get mask in al
  2146.     jz      @gpc_no_right1bits      ; skip if no pixels to set
  2147.  
  2148.     mov     ah, [ebp].gpc_colorf ; get foreground color
  2149.     out     dx, al               ; set up screen mask
  2150.     mov     [edi], ah            ; write foreground color
  2151.  
  2152. @gpc_no_right1bits:
  2153.  
  2154.     xor     al, ch               ; invert mask for background
  2155.     jz      @gpc_no_right0bits   ; hey, no need for this
  2156.  
  2157.     mov     ah, [ebp].gpc_colorb ; get background color
  2158.     out     dx, al               ; set up screen mask
  2159.     mov     [edi], ah            ; write foreground color
  2160.  
  2161. @gpc_no_right0bits:
  2162.     dec     edi                  ; adjust for next line advance
  2163.  
  2164. @gpc_next_line:
  2165.     add     edi, [ebp].gpc_width  ; point to next line
  2166.     xor     ch, char_bits       ; flip the clip mask back
  2167.  
  2168.     dec     [ebp].gpc_lines     ; count down lines
  2169.     jz      @gpc_exit           ; ok... done!
  2170.  
  2171.     jmp     @gpc_decode_char_byte   ; again! hey!
  2172.  
  2173. @gpc_exit:
  2174.     add     esp, 10             ; deallocate stack workspace
  2175.     pop     edi esi ebp         ; restore saved registers
  2176.     ret     10                  ; exit and clean up stack
  2177.  
  2178.     endif
  2179.     if x_tgprintc eq 1
  2180.  
  2181. ;==========================================
  2182. ;tgprintc (charnum%, xpos%, ypos%, colorf%)
  2183. ;==========================================
  2184. ;
  2185. ; transparently draws an ascii text character using the
  2186. ; currently selected 8x8 font on the active display page.
  2187. ;
  2188. ; entry: charnum = ascii character # to draw
  2189. ;        xpos    = x position to draw character at
  2190. ;        ypos    = y position of to draw character at
  2191. ;        colorf  = color to draw text character in
  2192. ;
  2193. ; exit:  no meaningful values returned
  2194. ;
  2195.  
  2196. tpc_stack   struc
  2197.     tpc_width   dd  ?   ; screen width-1
  2198.     tpc_lines   db  ?,? ; scan lines to decode
  2199.     tpc_t_sets  dd  ?   ; saved charset segment
  2200.                 dd  ?x3 ; edi, esi, ebp
  2201.                 dd  ?   ; caller
  2202.     tpc_colorf  db  ?,? ; text color
  2203.     tpc_ypos    dw  ?   ; y position to print at
  2204.     tpc_xpos    dw  ?   ; x position to print at
  2205.     tpc_char    db  ?,? ; character to print
  2206. tpc_stack   ends
  2207.  
  2208.         public tgprintc
  2209.  
  2210. tgprintc:
  2211.  
  2212.     push    ebp esi edi         ; preserve important registers
  2213.     sub     esp, 10             ; allocate workspace on stack
  2214.     mov     ebp, esp            ; set up stack frame
  2215.  
  2216.     mov     edi, current_page   ; point to active vga page
  2217.  
  2218.     movzx   eax, screen_width   ; get logical line width
  2219.     mov     ebx, eax            ; bx = screen width
  2220.     dec     bx                  ;    = screen width-1
  2221.     mov     [ebp].tpc_width,ebx ; save for later use
  2222.  
  2223.     mul     [ebp].tpc_ypos      ; start of line = ypos * width
  2224.     add     edi, eax            ; di -> start of line ypos
  2225.  
  2226.     movzx   eax, [ebp].tpc_xpos ; get xpos of character
  2227.     mov     cx, ax              ; save copy of xpos
  2228.     shr     ax, 2               ; bytes into line = xpos/4
  2229.     add     edi, eax            ; di -> (xpos, ypos)
  2230.  
  2231. ;get source addr of character bit map  & save
  2232.  
  2233.     mov     al, [ebp].tpc_char  ; get character #
  2234.     test    al, 080h            ; is hi bit set?
  2235.     jz      @tpc_lowchar        ; nope, use low char set ptr
  2236.  
  2237.     mov     ebx, charset_hi     ; bx = char set ptr:offset
  2238.     jmp     s @tpc_set_char     ; go setup character ptr
  2239.  
  2240. @tpc_lowchar:
  2241.  
  2242.     mov     ebx, charset_low    ; bx = char set ptr:offset
  2243.  
  2244. @tpc_set_char:
  2245.     and     eax, 07fh           ; mask out hi bits
  2246.     shl     ax, 3               ; * 8 bytes per bitmap
  2247.     add     ebx, eax            ; bx = offset of selected char
  2248.     mov     [ebp].tpc_t_sets, ebx ; save segment on stack
  2249.  
  2250.     and     cx, plane_bits      ; get plane #
  2251.     mov     ch, all_planes      ; get initial plane mask
  2252.     shl     ch, cl              ; and shift into position
  2253.     and     ch, all_planes      ; and mask to lower nibble
  2254.  
  2255.     mov     al, 04              ; 4-plane # = # of initial
  2256.     sub     al, cl              ; shifts to align bit mask
  2257.     mov     cl, al              ; shift count for shl
  2258.  
  2259. ;get segment of character map
  2260.  
  2261.     out_8   sc_index, map_mask  ; setup plane selections
  2262.     inc     dx                  ; dx -> sc_data
  2263.  
  2264.     mov     al, 08              ; 8 lines to process
  2265.     mov     [ebp].tpc_lines, al ; save on stack
  2266.  
  2267. @tpc_decode_char_byte:
  2268.  
  2269.     mov     esi, [ebp].tpc_t_sets ; get esi = string
  2270.  
  2271.     mov     bh, [esi]           ; get bit map
  2272.     inc     esi                 ; point to next line
  2273.     mov     [ebp].tpc_t_sets, esi ; and save new pointer...
  2274.  
  2275.     clr     eax                 ; clear ax
  2276.  
  2277.     clr     bl                      ; clear bl
  2278.     rol     bx, cl                  ; bl holds left edge bits
  2279.     movzx   esi, bx                 ; use as table index
  2280.     and     si, char_bits           ; get low bits
  2281.     mov     al, char_plane_data[esi] ; get mask in al
  2282.     jz      @tpc_no_left1bits       ; skip if no pixels to set
  2283.  
  2284.     mov     ah, [ebp].tpc_colorf ; get foreground color
  2285.     out     dx, al               ; set up screen mask
  2286.     mov     [edi], ah            ; write foreground color
  2287.  
  2288. ;now do middle/last band
  2289.  
  2290. @tpc_no_left1bits:
  2291.     inc     edi                 ; point to next byte
  2292.     rol     bx, 4               ; shift 4 bits
  2293.  
  2294.     movzx   esi, bx                 ; make lookup pointer
  2295.     and     si, char_bits           ; get low bits
  2296.     mov     al, char_plane_data[esi] ; get mask in al
  2297.     jz      @tpc_no_middle1bits     ; skip if no pixels to set
  2298.  
  2299.     mov     ah, [ebp].tpc_colorf ; get foreground color
  2300.     out     dx, al               ; set up screen mask
  2301.     mov     [edi], ah            ; write foreground color
  2302.  
  2303. @tpc_no_middle1bits:
  2304.     xor     ch, all_planes      ; invert clip mask
  2305.     cmp     cl, 4               ; aligned by 4?
  2306.     jz      @tpc_next_line      ; if so, exit now..
  2307.  
  2308.     inc     edi                 ; point to next byte
  2309.     rol     bx, 4               ; shift 4 bits
  2310.  
  2311.     movzx   esi, bx                 ; make lookup pointer
  2312.     and     si, char_bits           ; get low bits
  2313.     mov     al, char_plane_data[esi] ; get mask in al
  2314.     jz      @tpc_no_right1bits      ; skip if no pixels to set
  2315.  
  2316.     mov     ah, [ebp].tpc_colorf ; get foreground color
  2317.     out     dx, al               ; set up screen mask
  2318.     mov     [edi], ah            ; write foreground color
  2319.  
  2320. @tpc_no_right1bits:
  2321.     dec     edi                  ; adjust for next line advance
  2322.  
  2323. @tpc_next_line:
  2324.     add     edi, [ebp].tpc_width  ; point to next line
  2325.     xor     ch, char_bits       ; flip the clip mask back
  2326.  
  2327.     dec     [ebp].tpc_lines     ; count down lines
  2328.     jz      @tpc_exit           ; ok... done!
  2329.  
  2330.     jmp     @tpc_decode_char_byte   ; again! hey!
  2331.  
  2332. @tpc_exit:
  2333.     add     esp, 10             ; deallocate stack workspace
  2334.     pop     edi esi ebp         ; restore saved registers
  2335.     ret     8                   ; exit and clean up stack
  2336.  
  2337.     endif
  2338.     if x_gprintc eq 1
  2339.  
  2340. ;===============================================================
  2341. ;print_str (seg string, maxlen%, xpos%, ypos%, colorf%, colorb%)
  2342. ;===============================================================
  2343. ;
  2344. ; routine to quickly print a null terminated ascii string on the
  2345. ; active display page up to a maximum length.
  2346. ;
  2347. ; entry: string  = far pointer to ascii string to print
  2348. ;        maxlen  = # of characters to print if no null found
  2349. ;        xpos    = x position to draw text at
  2350. ;        ypos    = y position of to draw text at
  2351. ;        colorf  = color to draw text in
  2352. ;        colorb  = color to set background to
  2353. ;
  2354. ; exit:  no meaningful values returned
  2355. ;
  2356.  
  2357. ps_stack    struc
  2358.                 dd  ?x3 ; edi, esi, ebp
  2359.                 dd  ?   ; caller
  2360.     ps_colorb   dw  ?   ; background color
  2361.     ps_colorf   dw  ?   ; text color
  2362.     ps_ypos     dw  ?   ; y position to print at
  2363.     ps_xpos     dw  ?   ; x position to print at
  2364.     ps_len      dw  ?   ; maximum length of string to print
  2365.     ps_text     dd  ?   ; far ptr to text string
  2366. ps_stack    ends
  2367.  
  2368.         public  print_str
  2369.  
  2370. print_str:
  2371.  
  2372.     push    ebp esi edi         ; preserve important registers
  2373.     mov     ebp, esp            ; set up stack frame
  2374.  
  2375. @ps_print_it:
  2376.  
  2377.     mov     cx, [ebp].ps_len    ; get remaining text length
  2378.     jcxz    @ps_exit            ; exit when out of text
  2379.  
  2380.     mov     edi, [ebp].ps_text  ; edi -> current char in text
  2381.     mov     al, [edi]           ; al = text character
  2382.     and     ax, 00ffh           ; clear high word
  2383.     jz      @ps_exit            ; exit if null character
  2384.  
  2385.     dec     [ebp].ps_len        ; remaining text length--
  2386.     inc     [ebp].ps_text       ; point to next text char
  2387.  
  2388. ; set up call to gprintc
  2389.  
  2390.     push    ax                  ; set character parameter
  2391.     mov     bx, [ebp].ps_xpos   ; get xpos
  2392.     push    bx                  ; set xpos parameter
  2393.     add     bx, 8               ; advance 1 char to right
  2394.     mov     [ebp].ps_xpos, bx   ; save for next time through
  2395.  
  2396.     mov     bx, [ebp].ps_ypos   ; get ypos
  2397.     push    bx                  ; set ypos parameter
  2398.  
  2399.     mov     bx, [ebp].ps_colorf ; get text color
  2400.     push    bx                  ; set colorf parameter
  2401.  
  2402.     mov     bx, [ebp].ps_colorb ; get background color
  2403.     push    bx                  ; set colorb parameter
  2404.  
  2405.     call    gprintc             ; print character!
  2406.     jmp     s @ps_print_it      ; process next character
  2407.  
  2408. @ps_exit:
  2409.     pop     edi esi ebp         ; restore saved registers
  2410.     ret     14                  ; exit and clean up stack
  2411.  
  2412.     endif
  2413.     if x_tgprintc eq 1
  2414.  
  2415. ;================================================================
  2416. ;tprint_str (seg string, maxlen%, xpos%, ypos%, colorf%, colorb%)
  2417. ;================================================================
  2418. ;
  2419. ; routine to quickly transparently print a null terminated ascii
  2420. ; string on the active display page up to a maximum length.
  2421. ;
  2422. ; entry: string  = far pointer to ascii string to print
  2423. ;        maxlen  = # of characters to print if no null found
  2424. ;        xpos    = x position to draw text at
  2425. ;        ypos    = y position of to draw text at
  2426. ;        colorf  = color to draw text in
  2427. ;
  2428. ; exit:  no meaningful values returned
  2429. ;
  2430.  
  2431. tps_stack   struc
  2432.                 dd  ?x3 ; edi, esi, ebp
  2433.                 dd  ?   ; caller
  2434.     tps_colorf  dw  ?   ; text color
  2435.     tps_ypos    dw  ?   ; y position to print at
  2436.     tps_xpos    dw  ?   ; x position to print at
  2437.     tps_len     dw  ?   ; maximum length of string to print
  2438.     tps_text    dd  ?   ; far ptr to text string
  2439. tps_stack   ends
  2440.  
  2441.         public  tprint_str
  2442.  
  2443. tprint_str:
  2444.  
  2445.     push    ebp esi edi         ; preserve important registers
  2446.     mov     ebp, esp            ; set up stack frame
  2447.  
  2448. @ts_print_it:
  2449.  
  2450.     mov     cx, [ebp].tps_len   ; get remaining text length
  2451.     jcxz    @ts_exit            ; exit when out of text
  2452.  
  2453.     mov     edi, [ebp].tps_text ; edi -> current char in text
  2454.     mov     al, [edi]           ; al = text character
  2455.     and     ax, 00ffh           ; clear high word
  2456.     jz      @ts_exit            ; exit if null character
  2457.  
  2458.     dec     [ebp].tps_len       ; remaining text length--
  2459.     inc     [ebp].tps_text      ; point to next text char
  2460.  
  2461. ; set up call to tgprintc
  2462.  
  2463.     push    ax                  ; set character parameter
  2464.     mov     bx, [ebp].tps_xpos  ; get xpos
  2465.     push    bx                  ; set xpos parameter
  2466.     add     bx, 8               ; advance 1 char to right
  2467.     mov     [ebp].tps_xpos, bx  ; save for next time through
  2468.  
  2469.     mov     bx, [ebp].tps_ypos  ; get ypos
  2470.     push    bx                  ; set ypos parameter
  2471.  
  2472.     mov     bx, [ebp].tps_colorf ; get text color
  2473.     push    bx                  ; set colorf parameter
  2474.  
  2475.     call    tgprintc            ; print character!
  2476.     jmp     s @ts_print_it      ; process next character
  2477.  
  2478. @ts_exit:
  2479.     pop     edi esi ebp         ; restore saved registers
  2480.     ret     12                  ; exit and clean up stack
  2481.  
  2482.     endif
  2483.  
  2484. ;===========================================
  2485. ;set_display_font(seg fontdata, fontnumber%)
  2486. ;===========================================
  2487. ;
  2488. ; allows the user to specify their own font data for
  2489. ; wither the lower or upper 128 characters.
  2490. ;
  2491. ; entry: fontdata   = far pointer to font bitmaps
  2492. ;        fontnumber = which half of set this is
  2493. ;                   = 0, lower 128 characters
  2494. ;                   = 1, upper 128 characters
  2495. ;
  2496. ; exit:  no meaningful values returned
  2497. ;
  2498.  
  2499. sdf_stack   struc
  2500.                 dd  ?   ; ebp
  2501.                 dd  ?   ; caller
  2502.     sdf_which   dw  ?   ; hi table/low table flag
  2503.     sdf_font    dd  ?   ; far ptr to font table
  2504. sdf_stack   ends
  2505.  
  2506.     public  set_display_font
  2507.  
  2508. set_display_font:
  2509.  
  2510.     push    ebp                 ; preserve registers
  2511.     mov     ebp, esp            ; set up stack frame
  2512.  
  2513.     mov     edi, [ebp].sdf_font ; get far ptr to font
  2514.  
  2515.     mov     esi, o charset_low  ; assume lower 128 chars
  2516.     test    [ebp].sdf_which, 1  ; font #1 selected?
  2517.     jz      @sdf_set_font       ; if not, skip ahead
  2518.  
  2519.     mov     esi, o charset_hi   ; ah, really it's 128-255
  2520.  
  2521. @sdf_set_font:
  2522.     mov     [esi], edi          ; set font pointer offset
  2523.  
  2524.     pop     ebp                 ; restore registers
  2525.     ret     6                   ; we are done.. outa here
  2526.  
  2527. ; ===== bitmap (sprite) display routines =====
  2528.  
  2529. ;======================================================
  2530. ;draw_bitmap (seg image, xpos%, ypos% )
  2531. ;======================================================
  2532. ;
  2533. ; draws a variable sized graphics bitmap such as a
  2534. ; picture or an icon on the current display page in
  2535. ; mode x.  the bitmap is stored in a lifar byte array
  2536. ; corresponding to (0,0) (1,0), (2,0) .. (width, height)
  2537. ; this is the same lifar manner as mode 13h graphics.
  2538. ;
  2539. ; entry: image  = far pointer to bitmap data
  2540. ;        xpos   = x position to place upper left pixel at
  2541. ;        ypos   = y position to place upper left pixel at
  2542. ;        width  = width of the bitmap in pixels  - ommitted
  2543. ;        height = height of the bitmap in pixels - ommitted
  2544. ;
  2545. ; exit:  no meaningful values returned
  2546. ;
  2547. ; routine has been modified so that first two words of bitmap define
  2548. ; bitmap x and y size
  2549. ;
  2550.  
  2551. db_stack    struc
  2552.     db_lineo    dw  ?   ; offset to next line
  2553.     db_pixcount dw  ?   ; (minimum) # of pixels/line
  2554.     db_start    dd  ?   ; addr of upper left pixel
  2555.     db_pixskew  dw  ?   ; # of bytes to adjust eol
  2556.     db_skewflag dw  ?   ; extra pix on plane flag
  2557.     db_height   dw  ?   ; height of bitmap in pixels
  2558.                 dd  ?x3 ; edi, esi, ebp
  2559.                 dd  ?   ; caller
  2560.     db_ypos     dw  ?   ; y position to draw bitmap at
  2561.     db_xpos     dw  ?   ; x position to draw bitmap at
  2562.     db_image    dd  ?   ; far pointer to graphics bitmap
  2563. db_stack    ends
  2564.  
  2565.         public    draw_bitmap
  2566.  
  2567. draw_bitmap:
  2568.  
  2569.     push    ebp esi edi         ; preserve important registers
  2570.     sub     esp, 14             ; allocate workspace
  2571.     mov     ebp, esp            ; set up stack frame
  2572.  
  2573.     mov     edi, current_page   ; point to active vga page
  2574.     cld                         ; direction flag = forward
  2575.  
  2576.     movzx   eax, [ebp].db_ypos  ; get ul corner ypos
  2577.     mul     screen_width        ; ax = offset to line ypos
  2578.  
  2579.     movzx   ebx, [ebp].db_xpos  ; get ul corner xpos
  2580.     mov     cl, bl              ; save plane # in cl
  2581.     shr     bx, 2               ; xpos/4 = offset into line
  2582.  
  2583.     add     edi, eax            ; edi -> start of line
  2584.     add     edi, ebx            ; edi -> upper left pixel
  2585.     mov     [ebp].db_start, edi ; save starting addr
  2586.  
  2587. ; compute line to line offset
  2588.  
  2589.     mov     esi, [ebp].db_image ; esi-> source image
  2590.     lodsw                       ; get x width
  2591.     mov     bx,ax
  2592.     lodsw
  2593.     mov     [ebp].db_height,ax
  2594.     add     [ebp].db_image,4
  2595.  
  2596.     mov     dx, bx              ; save copy in dx
  2597.     shr     bx, 2               ; /4 = width in bands
  2598.     mov     ax, screen_width    ; get screen width
  2599.     sub     ax, bx              ; - (bitmap width/4)
  2600.  
  2601.     mov     [ebp].db_lineo, ax      ; save line width offset
  2602.     mov     [ebp].db_pixcount, bx   ; minimum # pix to copy
  2603.  
  2604.     and     dx, plane_bits          ; get "partial band" size (0-3)
  2605.     mov     [ebp].db_pixskew, dx    ; also end of line skew
  2606.     mov     [ebp].db_skewflag, dx   ; save as flag/count
  2607.  
  2608.     and     cx, plane_bits      ; cl = starting plane #
  2609.     mov     ax, map_mask_plane2 ; plane mask & plane select
  2610.     shl     ah, cl              ; select correct plane
  2611.     out_16  sc_index, ax        ; select plane...
  2612.     mov     bh, ah              ; bh = saved plane mask
  2613.     mov     bl, 4               ; bl = planes to copy
  2614.  
  2615. @db_copy_plane:
  2616.  
  2617.     mov     esi, [ebp].db_image ; esi-> source image
  2618.     mov     dx, [ebp].db_height ; # of lines to copy
  2619.     mov     edi, [ebp].db_start ; edi-> dest pos
  2620.  
  2621. @db_copy_line:
  2622.     mov     cx, [ebp].db_pixcount   ; min # to copy
  2623.  
  2624.     test    cl, 0fch            ; 16+pixwide?
  2625.     jz      @db_copy_remainder  ; nope...
  2626.  
  2627. ; pixel copy loop has been unrolled to x4
  2628.  
  2629. @db_copy_loop:
  2630.     movsb                       ; copy bitmap pixel
  2631.     add     esi, 3              ; skip to next byte in same plane
  2632.     movsb                       ; copy bitmap pixel
  2633.     add     esi, 3              ; skip to next byte in same plane
  2634.     movsb                       ; copy bitmap pixel
  2635.     add     esi, 3              ; skip to next byte in same plane
  2636.     movsb                       ; copy bitmap pixel
  2637.     add     esi, 3              ; skip to next byte in same plane
  2638.  
  2639.     sub     cl, 4               ; pixels to copy=-4
  2640.     test    cl, 0fch            ; 4+ pixels left?
  2641.     jnz     @db_copy_loop       ; if so, do another block
  2642.  
  2643. @db_copy_remainder:
  2644.     jcxz    @db_next_line       ; any pixels left on line
  2645.  
  2646. @db_copy2:
  2647.     movsb                       ; copy bitmap pixel
  2648.     add     esi,3               ; skip to next byte in same plane
  2649.     loopx   cx, @db_copy2       ; pixels to copy--, loop until done
  2650.  
  2651. @db_next_line:
  2652.  
  2653. ; any partial pixels? (some planes only)
  2654.  
  2655.     or      cx, [ebp].db_skewflag   ; get skew count
  2656.     jz      @db_next2               ; if no partial pixels
  2657.  
  2658.     movsb                       ; copy bitmap pixel
  2659.     dec     edi                 ; back up to align
  2660.     dec     esi                 ; back up to align
  2661.  
  2662. @db_next2:
  2663.     movzx   eax, [ebp].db_pixskew ; adjust skew
  2664.     add     esi, eax
  2665.     movzx   eax, [ebp].db_lineo   ; set to next display line
  2666.     add     edi, eax
  2667.     loopx   dx, @db_copy_line   ; lines to copy--, loop if more
  2668.  
  2669. ; copy next plane....
  2670.  
  2671.     dec     bl                  ; planes to go--
  2672.     jz      @db_exit            ; hey! we are done
  2673.  
  2674.     rol     bh, 1               ; next plane in line...
  2675.     out_8   sc_data, bh         ; select plane
  2676.  
  2677.     cmp     al, 12h             ; carry set if al=11h
  2678.     adc     [ebp].db_start, 0   ; screen addr =+carry
  2679.     inc     w [ebp].db_image    ; start @ next byte
  2680.  
  2681.     sub     [ebp].db_skewflag, 1 ; reduce planes to skew
  2682.     adc     [ebp].db_skewflag, 0 ; back to 0 if it was -1
  2683.  
  2684.     jmp     f @db_copy_plane    ; go copy the next plane
  2685.  
  2686. @db_exit:
  2687.     add     esp, 14             ; deallocate workspace
  2688.     pop     edi esi ebp         ; restore saved registers
  2689.     ret     8                   ; exit and clean up stack
  2690.  
  2691. ;=======================================================
  2692. ;tdraw_bitmap (seg image, xpos%, ypos%)
  2693. ;=======================================================
  2694. ;
  2695. ; transparently draws a variable sized graphics bitmap
  2696. ; such as a picture or an icon on the current display page
  2697. ; in mode x.  pixels with a value of 0 are not drawn,
  2698. ; leaving the previous "background" contents intact.
  2699. ;
  2700. ; the bitmap format is the same as for the draw_bitmap function.
  2701. ;
  2702. ; entry: image  = far pointer to bitmap data
  2703. ;        xpos   = x position to place upper left pixel at
  2704. ;        ypos   = y position to place upper left pixel at
  2705. ;        width  = width of the bitmap in pixels   - ommitted
  2706. ;        height = height of the bitmap in pixels  - ommitted
  2707. ;
  2708. ; exit:  no meaningful values returned
  2709. ;
  2710. ; routine has been modified so that first two words of bitmap define
  2711. ; bitmap x and y size
  2712. ;
  2713.  
  2714. tb_stack    struc
  2715.     tb_lineo    dw  ?   ; offset to next line
  2716.     tb_pixcount dw  ?   ; (minimum) # of pixels/line
  2717.     tb_start    dd  ?   ; addr of upper left pixel
  2718.     tb_pixskew  dw  ?   ; # of bytes to adjust eol
  2719.     tb_skewflag dw  ?   ; extra pix on plane flag
  2720.     tb_height   dw  ?   ; height of bitmap in pixels
  2721.                 dd  ?x3 ; edi, esi, ebp
  2722.                 dd  ?   ; caller
  2723.     tb_ypos     dw  ?   ; y position to draw bitmap at
  2724.     tb_xpos     dw  ?   ; x position to draw bitmap at
  2725.     tb_image    dd  ?   ; far pointer to graphics bitmap
  2726. tb_stack    ends
  2727.  
  2728.         public    tdraw_bitmap
  2729.  
  2730. tdraw_bitmap:
  2731.  
  2732.     push    ebp esi edi         ; preserve important registers
  2733.     sub     esp, 14             ; allocate workspace
  2734.     mov     ebp, esp            ; set up stack frame
  2735.  
  2736.     mov     edi, current_page   ; point to active vga page
  2737.     cld                         ; direction flag = forward
  2738.  
  2739.     movzx   eax, [ebp].tb_ypos  ; get ul corner ypos
  2740.     mul     screen_width        ; ax = offset to line ypos
  2741.  
  2742.     movzx   ebx, [ebp].tb_xpos  ; get ul corner xpos
  2743.     mov     cl, bl              ; save plane # in cl
  2744.     shr     bx, 2               ; xpos/4 = offset into line
  2745.  
  2746.     add     edi, eax            ; edi -> start of line
  2747.     add     edi, ebx            ; edi -> upper left pixel
  2748.     mov     [ebp].tb_start, edi ; save starting addr
  2749.  
  2750. ; compute line to line offset
  2751.  
  2752.     mov     esi, [ebp].tb_image ; esi-> source image
  2753.     lodsw                       ; get x width
  2754.     mov     bx,ax
  2755.     lodsw
  2756.     mov     [ebp].tb_height,ax
  2757.     add     [ebp].tb_image,4
  2758.  
  2759.     mov     dx, bx              ; save copy in dx
  2760.     shr     bx, 2               ; /4 = width in bands
  2761.     mov     ax, screen_width    ; get screen width
  2762.     sub     ax, bx              ; - (bitmap width/4)
  2763.  
  2764.     mov     [ebp].tb_lineo, ax      ; save line width offset
  2765.     mov     [ebp].tb_pixcount, bx   ; minimum # pix to copy
  2766.  
  2767.     and     dx, plane_bits          ; get "partial band" size (0-3)
  2768.     mov     [ebp].tb_pixskew, dx    ; also end of line skew
  2769.     mov     [ebp].tb_skewflag, dx   ; save as flag/count
  2770.  
  2771.     and     cx, plane_bits      ; cl = starting plane #
  2772.     mov     ax, map_mask_plane2 ; plane mask & plane select
  2773.     shl     ah, cl              ; select correct plane
  2774.     out_16  sc_index, ax        ; select plane...
  2775.     mov     bh, ah              ; bh = saved plane mask
  2776.     mov     bl, 4               ; bl = planes to copy
  2777.  
  2778. @tb_copy_plane:
  2779.  
  2780.     mov     esi, [ebp].tb_image  ; esi-> source image
  2781.     mov     dx, [ebp].tb_height  ; # of lines to copy
  2782.     mov     edi, [ebp].tb_start  ; edi-> dest pos
  2783.  
  2784. ; here ah is set with the value to be considered
  2785. ; "transparent".  it can be changed!
  2786.  
  2787. @tb_copy_line:
  2788.     mov     ah, 0               ; value to detect 0
  2789.     mov     cx, [ebp].tb_pixcount    ; min # to copy
  2790.  
  2791.     test    cl, 0fch            ; 16+pixwide?
  2792.     jz      @tb_copy_remainder  ; nope...
  2793.  
  2794. ; pixel copy loop has been unrolled to x4
  2795.  
  2796. @tb_copy_loop:
  2797.     lodsb                       ; get pixel value in al
  2798.     add     esi, 3              ; skip to next byte in same plane
  2799.     cmp     al, ah              ; it is "transparent"?
  2800.     je      @tb_skip_01         ; skip ahead if so
  2801.     mov     [edi], al           ; copy pixel to vga screen
  2802.  
  2803. @tb_skip_01:
  2804.     lodsb                       ; get pixel value in al
  2805.     add     esi, 3              ; skip to next byte in same plane
  2806.     cmp     al, ah              ; it is "transparent"?
  2807.     je      @tb_skip_02         ; skip ahead if so
  2808.     mov     [edi+1], al         ; copy pixel to vga screen
  2809.  
  2810. @tb_skip_02:
  2811.     lodsb                       ; get pixel value in al
  2812.     add     esi, 3              ; skip to next byte in same plane
  2813.     cmp     al, ah              ; it is "transparent"?
  2814.     je      @tb_skip_03         ; skip ahead if so
  2815.     mov     [edi+2], al         ; copy pixel to vga screen
  2816.  
  2817. @tb_skip_03:
  2818.     lodsb                       ; get pixel value in al
  2819.     add     esi, 3              ; skip to next byte in same plane
  2820.     cmp     al, ah              ; it is "transparent"?
  2821.     je      @tb_skip_04         ; skip ahead if so
  2822.     mov     [edi+3], al         ; copy pixel to vga screen
  2823.  
  2824. @tb_skip_04:
  2825.     add     edi, 4              ; adjust pixel write location
  2826.     sub     cl, 4               ; pixels to copy=-4
  2827.     test    cl, 0fch            ; 4+ pixels left?
  2828.     jnz     @tb_copy_loop       ; if so, do another block
  2829.  
  2830. @tb_copy_remainder:
  2831.     jcxz    @tb_next_line       ; any pixels left on line
  2832.  
  2833. @tb_copy2:
  2834.     lodsb                       ; get pixel value in al
  2835.     add     esi, 3              ; skip to next byte in same plane
  2836.     cmp     al, ah              ; it is "transparent"?
  2837.     je      @tb_skip_05         ; skip ahead if so
  2838.     mov     [edi], al           ; copy pixel to vga screen
  2839.  
  2840. @tb_skip_05:
  2841.     inc     edi                 ; advance dest addr
  2842.     loopx   cx, @tb_copy2       ; pixels to copy--, loop until done
  2843.  
  2844. @tb_next_line:
  2845.  
  2846. ; any partial pixels? (some planes only)
  2847.  
  2848.     or      cx, [ebp].tb_skewflag   ; get skew count
  2849.     jz      @tb_next2               ; if no partial pixels
  2850.  
  2851.     lodsb                       ; get pixel value in al
  2852.     dec     esi                 ; backup to align
  2853.     cmp     al, ah              ; it is "transparent"?
  2854.     je      @tb_next2           ; skip ahead if so
  2855.     mov     [edi], al           ; copy pixel to vga screen
  2856.  
  2857. @tb_next2:
  2858.     movzx   eax, [ebp].tb_pixskew ; adjust skew
  2859.     add     esi, eax
  2860.     movzx   eax, [ebp].tb_lineo   ; set to next display line
  2861.     add     edi, eax
  2862.     loopx   dx, @tb_copy_line   ; lines to copy--, loop if more
  2863.  
  2864.     ;copy next plane....
  2865.  
  2866.     dec     bl                  ; planes to go--
  2867.     jz      @tb_exit            ; hey! we are done
  2868.  
  2869.     rol     bh, 1               ; next plane in line...
  2870.     out_8   sc_data, bh         ; select plane
  2871.  
  2872.     cmp     al, 12h             ; carry set if al=11h
  2873.     adc     [ebp].tb_start, 0   ; screen addr =+carry
  2874.     inc     w [ebp].tb_image    ; start @ next byte
  2875.  
  2876.     sub     [ebp].tb_skewflag, 1 ; reduce planes to skew
  2877.     adc     [ebp].tb_skewflag, 0 ; back to 0 if it was -1
  2878.  
  2879.     jmp     @tb_copy_plane      ; go copy the next plane
  2880.  
  2881. @tb_exit:
  2882.     add     esp, 14             ; deallocate workspace
  2883.     pop     edi esi ebp         ; restore saved registers
  2884.     ret     8                   ; exit and clean up stack
  2885.  
  2886. ; ==== video memory to video memory copy routines =====
  2887.  
  2888. ;==================================
  2889. ;copy_page (sourcepage%, destpage%)
  2890. ;==================================
  2891. ;
  2892. ; duplicate on display page onto another
  2893. ;
  2894. ; entry: sourcepage = display page # to duplicate
  2895. ;        destpage   = display page # to hold copy
  2896. ;
  2897. ; exit:  no meaningful values returned
  2898. ;
  2899.  
  2900. cp_stack    struc
  2901.                 dd  ?x3 ; edi, esi, ebp
  2902.                 dd  ?   ; caller
  2903.     cp_destp    dw  ?   ; page to hold copied image
  2904.     cp_sourcep  dw  ?   ; page to make copy from
  2905. cp_stack    ends
  2906.  
  2907.         public    copy_page
  2908.  
  2909. copy_page:
  2910.  
  2911.     push    ebp esi edi         ; preserve important registers
  2912.     mov     ebp, esp            ; set up stack frame
  2913.     cld                         ; block xfer forwards
  2914.  
  2915. ; make sure page #'s are valid
  2916.  
  2917.     mov     ax, [ebp].cp_sourcep ; get source page #
  2918.     cmp     ax, last_page       ; is it > max page #?
  2919.     jae     @cp_exit            ; if so, abort
  2920.  
  2921.     mov     bx, [ebp].cp_destp  ; get destination page #
  2922.     cmp     bx, last_page       ; is it > max page #?
  2923.     jae     @cp_exit            ; if so, abort
  2924.  
  2925.     cmp     ax, bx              ; pages #'s the same?
  2926.     je      @cp_exit            ; if so, abort
  2927.  
  2928. ; setup esi and edi to video pages
  2929.  
  2930.     shl     bx, 2               ; scale index to dword
  2931.     mov     edi, page_addr[ebx] ; offset to dest page
  2932.  
  2933.     mov     bx, ax              ; index to source page
  2934.     shl     bx, 2               ; scale index to dword
  2935.     mov     esi, page_addr[ebx] ; offset to source page
  2936.  
  2937.     movzx   ecx, page_size      ; get size of page
  2938.  
  2939. ; setup vga registers for mem to mem copy
  2940.  
  2941.     out_16  gc_index, latches_on    ; data from latches = on
  2942.     out_16  sc_index, all_planes_on ; copy all planes
  2943.  
  2944. ; note.. do *not* use movsw or movsd - they will
  2945. ; screw with the latches which are 8 bits x 4
  2946.  
  2947.     rep     movsb               ; copy entire page!
  2948.  
  2949. ; reset vga for normal memory access
  2950.  
  2951.     out_16  gc_index, latches_off   ; data from latches = off
  2952.  
  2953. @cp_exit:
  2954.     pop     edi esi ebp         ; restore saved registers
  2955.     ret     4                   ; exit and clean up stack
  2956.  
  2957. ;==========================================================================
  2958. ;copy_bitmap (sourcepage%, x1%, y1%, x2%, y2%, destpage%, destx1%, desty1%)
  2959. ;==========================================================================
  2960. ;
  2961. ; copies a bitmap image from one display page to another
  2962. ; this routine is limited to copying images with the same
  2963. ; plane alignment.  to work: (x1 mod 4) must = (destx1 mod 4)
  2964. ; copying an image to the same page is supported, but results
  2965. ; may be defined when the when the rectangular areas
  2966. ; (x1, y1) - (x2, y2) and (destx1, desty1) -
  2967. ; (destx1+(x2-x1), desty1+(y2-y1)) overlap...
  2968. ; no paramter checking to done to insure that
  2969. ; x2 >= x1 and y2 >= y1.  be careful...
  2970. ;
  2971. ; entry: sourcepage = display page # with source image
  2972. ;        x1         = upper left xpos of source image
  2973. ;        y1         = upper left ypos of source image
  2974. ;        x2         = lower right xpos of source image
  2975. ;        y2         = lower right ypos of source image
  2976. ;        destpage   = display page # to copy image to
  2977. ;        destx1     = xpos to copy ul corner of image to
  2978. ;        desty1     = ypos to copy ul corner of image to
  2979. ;
  2980. ; exit:  ax = success flag:   0 = failure / -1= success
  2981. ;
  2982.  
  2983. cb_stack    struc
  2984.     cb_height   dw  ?   ; height of image in lines
  2985.     cb_width    dw  ?   ; width of image in "bands"
  2986.                 dd  ?x3 ; edi, esi, ebp
  2987.                 dd  ?   ; caller
  2988.     cb_desty1   dw  ?   ; destination ypos
  2989.     cb_destx1   dw  ?   ; destination xpos
  2990.     cb_destp    dw  ?   ; page to copy bitmap to
  2991.     cb_y2       dw  ?   ; lr ypos of image
  2992.     cb_x2       dw  ?   ; lr xpos of image
  2993.     cb_y1       dw  ?   ; ul ypos of image
  2994.     cb_x1       dw  ?   ; ul xpos of image
  2995.     cb_sourcep  dw  ?   ; page containing source bitmap
  2996. cb_stack    ends
  2997.  
  2998.         public    copy_bitmap
  2999.  
  3000. copy_bitmap:
  3001.  
  3002.     push    ebp esi edi         ; preserve important registers
  3003.     sub     esp, 4              ; allocate workspace on stack
  3004.     mov     ebp, esp            ; set up stack frame
  3005.  
  3006. ; prep registers (and keep jumps short!)
  3007.  
  3008.     cld                         ; block xfer forwards
  3009.  
  3010. ; make sure parameters are valid
  3011.  
  3012.     movzx   ebx, [ebp].cb_sourcep ; get source page #
  3013.     cmp     bx, last_page       ; is it > max page #?
  3014.     jae     @cb_abort           ; if so, abort
  3015.  
  3016.     mov     cx, [ebp].cb_destp  ; get destination page #
  3017.     cmp     cx, last_page       ; is it > max page #?
  3018.     jae     @cb_abort           ; if so, abort
  3019.  
  3020.     mov     ax, [ebp].cb_x1     ; get source x1
  3021.     xor     ax, [ebp].cb_destx1 ; compare bits 0-1
  3022.     and     ax, plane_bits      ; check plane bits
  3023.     jnz     @cb_abort           ; they should cancel out
  3024.  
  3025. ; setup for copy processing
  3026.  
  3027.     out_8   sc_index, map_mask      ; set up for plane select
  3028.     out_16  gc_index, latches_on    ; data from latches = on
  3029.  
  3030. ; compute info about images, setup esi & edi
  3031.  
  3032.     mov     ax, [ebp].cb_y2     ; height of bitmap in lines
  3033.     sub     ax, [ebp].cb_y1     ; is y2 - y1 + 1
  3034.     inc     ax                  ; (add 1 since were not 0 based)
  3035.     mov     [ebp].cb_height, ax ; save on stack for later use
  3036.  
  3037.     mov     ax, [ebp].cb_x2     ; get # of "bands" of 4 pixels
  3038.     mov     dx, [ebp].cb_x1     ; the bitmap occupies as x2-x1
  3039.     shr     ax, 2               ; get x2 band (x2 / 4)
  3040.     shr     dx, 2               ; get x1 band (x1 / 4)
  3041.     sub     ax, dx              ; ax = # of bands - 1
  3042.     inc     ax                  ; ax = # of bands
  3043.     mov     [ebp].cb_width, ax  ; save on stack for later use
  3044.  
  3045.     shl     bx, 2               ; scale source page to dword
  3046.     mov     esi, page_addr[ebx] ; si = offset of source page
  3047.     mov     ax, [ebp].cb_y1     ; get source y1 line
  3048.     mul     screen_width        ; ax = offset to line y1
  3049.     movzx   eax, ax
  3050.     add     esi, eax            ; si = offset to line y1
  3051.     mov     ax, [ebp].cb_x1     ; get source x1
  3052.     shr     ax, 2               ; x1 / 4 = byte offset
  3053.     add     esi, eax            ; si = byte offset to (x1,y1)
  3054.  
  3055.     mov     bx, cx              ; dest page index to bx
  3056.     shl     bx, 2               ; scale source page to dword
  3057.     mov     edi, page_addr[ebx] ; di = offset of dest page
  3058.     mov     ax, [ebp].cb_desty1 ; get dest y1 line
  3059.     mul     screen_width        ; ax = offset to line y1
  3060.     movzx   eax, ax
  3061.     add     edi, eax            ; di = offset to line y1
  3062.     mov     ax, [ebp].cb_destx1 ; get dest x1
  3063.     shr     ax, 2               ; x1 / 4 = byte offset
  3064.     add     edi, eax            ; di = byte offset to (d-x1,d-y1)
  3065.  
  3066.     mov     cx, [ebp].cb_width  ; cx = width of image (bands)
  3067.     dec     cx                  ; cx = 1?
  3068.     je      @cb_only_one_band   ; 0 means image width of 1 band
  3069.  
  3070.     mov     bx, [ebp].cb_x1     ; get source x1
  3071.     and     bx, plane_bits      ; aligned? (bits 0-1 = 00?)
  3072.     jz      @cb_check_right     ; if so, check right alignment
  3073.     jnz     @cb_left_band       ; not aligned? well..
  3074.  
  3075. @cb_abort:
  3076.     clr     ax                  ; return false (failure)
  3077.     jmp     @cb_exit            ; and finish up
  3078.  
  3079. ; copy when left & right clip masks overlap...
  3080.  
  3081. @cb_only_one_band:
  3082.     mov     bx, [ebp].cb_x1         ; get left clip mask
  3083.     and     bx, plane_bits          ; mask out row #
  3084.     mov     al, left_clip_mask[ebx] ; get left edge mask
  3085.     mov     bx, [ebp].cb_x2         ; get right clip mask
  3086.     and     bx, plane_bits          ; mask out row #
  3087.     and     al, right_clip_mask[ebx] ; get right edge mask byte
  3088.  
  3089.     out_8   sc_data, al         ; clip for left & right masks
  3090.  
  3091.     mov     cx, [ebp].cb_height ; cx = # of lines to copy
  3092.     movzx   edx, screen_width   ; dx = width of screen
  3093.     clr     ebx                 ; bx = offset into image
  3094.  
  3095. @cb_one_loop:
  3096.     mov     al, [esi+ebx]       ; load latches
  3097.     mov     [edi+ebx], al       ; unload latches
  3098.     add     bx, dx              ; advance offset to next line
  3099.     loopjz  cx, @cb_one_done    ; exit loop if finished
  3100.  
  3101.     mov     al, [esi+ebx]       ; load latches
  3102.     mov     [edi+ebx], al       ; unload latches
  3103.     add     bx, dx              ; advance offset to next line
  3104.     loopx   cx, @cb_one_loop    ; loop until finished
  3105.  
  3106. @cb_one_done:
  3107.     jmp     @cb_finish          ; outa here!
  3108.  
  3109. ; copy left edge of bitmap
  3110.  
  3111. @cb_left_band:
  3112.  
  3113.     out_8   sc_data, left_clip_mask[ebx] ; set left edge plane mask
  3114.  
  3115.     mov     cx, [ebp].cb_height ; cx = # of lines to copy
  3116.     mov     dx, screen_width    ; dx = width of screen
  3117.     clr     ebx                 ; bx = offset into image
  3118.  
  3119. @cb_left_loop:
  3120.     mov     al, [esi+ebx]       ; load latches
  3121.     mov     [edi+ebx], al       ; unload latches
  3122.     add     bx, dx              ; advance offset to next line
  3123.     loopjz  cx, @cb_left_done   ; exit loop if finished
  3124.  
  3125.     mov     al, [esi+ebx]       ; load latches
  3126.     mov     [edi+ebx], al       ; unload latches
  3127.     add     bx, dx              ; advance offset to next line
  3128.     loopx   cx, @cb_left_loop   ; loop until finished
  3129.  
  3130. @cb_left_done:
  3131.     inc     edi                 ; move dest over 1 band
  3132.     inc     esi                 ; move source over 1 band
  3133.     dec     [ebp].cb_width      ; band width--
  3134.  
  3135. ; determine if right edge of bitmap needs special copy
  3136.  
  3137. @cb_check_right:
  3138.     mov     bx, [ebp].cb_x2     ; get source x2
  3139.     and     bx, plane_bits      ; aligned? (bits 0-1 = 11?)
  3140.     cmp     bl, 03h             ; plane = 3?
  3141.     je      @cb_copy_middle     ; copy the middle then!
  3142.  
  3143. ; copy right edge of bitmap
  3144.  
  3145. @cb_right_band:
  3146.  
  3147.     out_8   sc_data, right_clip_mask[ebx]  ; set right edge plane mask
  3148.  
  3149.     dec     [ebp].cb_width      ; band width--
  3150.     mov     cx, [ebp].cb_height ; cx = # of lines to copy
  3151.     mov     dx, screen_width    ; dx = width of screen
  3152.     movzx   ebx, [ebp].cb_width ; bx = offset to right edge
  3153.  
  3154. @cb_right_loop:
  3155.     mov     al, [esi+ebx]       ; load latches
  3156.     mov     [edi+ebx], al       ; unload latches
  3157.     add     bx, dx              ; advance offset to next line
  3158.     loopjz  cx, @cb_right_done  ; exit loop if finished
  3159.  
  3160.     mov     al, [esi+ebx]       ; load latches
  3161.     mov     [edi+ebx], al       ; unload latches
  3162.     add     bx, dx              ; advance offset to next line
  3163.     loopx   cx, @cb_right_loop  ; loop until finished
  3164.  
  3165. @cb_right_done:
  3166.  
  3167. ; copy the main block of the bitmap
  3168.  
  3169. @cb_copy_middle:
  3170.  
  3171.     mov     cx, [ebp].cb_width  ; get width remaining
  3172.     jcxz    @cb_finish          ; exit if done
  3173.  
  3174.     out_8   sc_data, all_planes ; copy all planes
  3175.  
  3176.     mov     dx, screen_width    ; get width of screen minus
  3177.     sub     dx, cx              ; image width (for adjustment)
  3178.     movzx   edx, dx
  3179.     mov     ax, [ebp].cb_height ; ax = # of lines to copy
  3180.     movzx   ecx,cx
  3181.     mov     ebx, ecx            ; bx = quick rep reload count
  3182.  
  3183. ; actual copy loop.  rep movsb does the work
  3184.  
  3185. @cb_middle_copy:
  3186.     mov     ecx, ebx            ; recharge rep count
  3187.     rep     movsb               ; move bands
  3188.     loopjz  ax, @cb_finish      ; exit loop if finished
  3189.  
  3190.     add     esi, edx            ; adjust esi to next line
  3191.     add     edi, edx            ; adjust edi to next line
  3192.  
  3193.     mov     ecx, ebx            ; recharge rep count
  3194.     rep     movsb               ; move bands
  3195.  
  3196.     add     esi, edx            ; adjust esi to next line
  3197.     add     edi, edx            ; adjust edi to next line
  3198.     loopx   ax, @cb_middle_copy ; copy lines until done
  3199.  
  3200. @cb_finish:
  3201.     out_16  gc_index, latches_off   ; data from latches = on
  3202.  
  3203. @cb_exit:
  3204.     add     esp, 4              ; deallocate stack workspace
  3205.     pop     edi esi ebp         ; restore saved registers
  3206.     ret     16                  ; exit and clean up stack
  3207.  
  3208. ; return to mode 03 before exiting to dos
  3209.  
  3210.         public mode03
  3211.  
  3212. mode03:
  3213.         mov v86r_ax,3h
  3214.         mov al,10h
  3215.         int 33h
  3216.         ret
  3217.  
  3218.         public wipeoffpalette
  3219.  
  3220. wipeoffpalette:
  3221.         xor     al,al   ; wipe palette, clear all to eax
  3222.         mov     dx,3c8h
  3223.         out     dx,al
  3224.         inc     dx
  3225.         mov     cx,768/3
  3226.         mov     ebx,eax
  3227.         mov     esi,eax
  3228.         mov     edi,eax
  3229.         shr     esi,8
  3230.         shr     edi,16
  3231.  
  3232. wipeit:
  3233.         mov     eax,edi
  3234.         out     dx,al
  3235.         mov     eax,esi
  3236.         out     dx,al
  3237.         mov     eax,ebx
  3238.         out     dx,al
  3239.         loop    wipeit
  3240.  
  3241.         ret
  3242.  
  3243.         public turn_screen_off
  3244.         public turn_screen_on
  3245.  
  3246. turn_screen_off:           ; guess what these do!
  3247.         mov dx,03dah       ; these are used when changing video modes
  3248.         in al,dx           ; to avoid any flicker
  3249.         mov dx,03c0h
  3250.         mov al,0
  3251.         out dx,al
  3252.         ret
  3253.  
  3254. turn_screen_on:
  3255.         mov dx,03dah
  3256.         in al,dx
  3257.         mov dx,03c0h
  3258.         mov al,20h
  3259.         out dx,al
  3260.         ret
  3261.  
  3262. code32  ends
  3263.         end
  3264.